iclassify-interface 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg
data/README ADDED
@@ -0,0 +1,31 @@
1
+ = iclassify-interface
2
+
3
+ http://github.com/loe/iclassify-interface
4
+
5
+ == DESCRIPTION:
6
+
7
+ Module for interfacing with iclassify.
8
+
9
+ == INSTALL:
10
+
11
+ sudo gem install loe-iclassify-interface
12
+
13
+ == LICENSE:
14
+
15
+ # Author:: Adam Jacob (<adam@hjksolutions.com>)
16
+ # Mods:: W. Andrew Loe III (<andrew@andrewloe.com>)
17
+ # Copyright:: Copyright (c) 2007 HJK Solutions, LLC
18
+ # License:: GNU General Public License version 2.1
19
+ #
20
+ # This program is free software; you can redistribute it and/or modify
21
+ # it under the terms of the GNU General Public License version 2.1
22
+ # as published by the Free Software Foundation.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License along
30
+ # with this program; if not, write to the Free Software Foundation, Inc.,
31
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "iclassify-interface"
8
+ gem.summary = %Q{Module for interfacing with iclassify.}
9
+ gem.email = "andrew@andrewloe.com"
10
+ gem.homepage = "http://github.com/loe/iclassify-interface"
11
+ gem.authors = ["Adam Jacob", "W. Andrew Loe III"]
12
+ gem.add_dependency("uuidtools", '>= 2.0.0')
13
+ gem.add_dependency("highline")
14
+ gem.add_dependency("builder")
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ if File.exist?('VERSION.yml')
48
+ config = YAML.load(File.read('VERSION.yml'))
49
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
50
+ else
51
+ version = ""
52
+ end
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "iclassify-interface #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
59
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
data/bin/icagent ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # iClassify - A node classification service.
4
+ # Copyright (C) 2007 HJK Solutions and Adam Jacob (<adam@hjksolutions.com>)
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ #
20
+ # icagent registers a node with iclassify, and lets you use small DSL for
21
+ # classifiying them.
22
+
23
+ require 'rubygems'
24
+ require File.dirname(__FILE__) + '/../lib/iclassify-interface'
25
+ require 'optparse'
26
+
27
+ config = {
28
+ :uuidfile => '/etc/icagent/icagent.uuid',
29
+ :directory => File.dirname(__FILE__) + '/../recipes'
30
+ }
31
+
32
+ opts = OptionParser.new do |opts|
33
+ opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)"
34
+ opts.on("-d DIRECTORY", "--directory DIRECTORY", "Path to icagent recipes") do |d|
35
+ config[:directory] = d
36
+ end
37
+ opts.on("-r RECIPE", "--recipe RECIPE", "Path to a single icagent recipe") do |r|
38
+ config[:recipe] = r
39
+ end
40
+ opts.on("-u UUIDFILE", "--uuidfile UUIDFILE", "Path to the uuid file") do |u|
41
+ config[:uuidfile] = u
42
+ end
43
+ opts.on("-s SERVER", "--server", "iClassify Server URL") do |s|
44
+ config[:server] = s
45
+ end
46
+ opts.on("-n", "--no-action", "Don't update anything, just print.") do |n|
47
+ config[:dryrun] = true
48
+ end
49
+ opts.on("-w WAIT", "--wait TIME", "Wait for up to TIME seconds.") do |w|
50
+ config[:wait] = w.to_i
51
+ end
52
+ opts.on_tail("-h", "--help", "Show this message") do
53
+ puts opts
54
+ exit
55
+ end
56
+ end
57
+ opts.parse!(ARGV)
58
+
59
+ unless config.has_key?(:recipe) || config.has_key?(:directory)
60
+ puts "You must specify either a recipe (-r) or a directory (-d)"
61
+ puts opts
62
+ exit
63
+ end
64
+
65
+ if config.has_key?(:wait)
66
+ splay = rand(config[:wait])
67
+ sleep(splay)
68
+ end
69
+
70
+ agent = IClassify::Agent.new(config[:uuidfile], config[:server])
71
+ begin
72
+ agent.load
73
+ rescue SocketError
74
+ $stderr.puts("Error: Cannot connect to server.")
75
+ exit 1
76
+ end
77
+
78
+ if config.has_key?(:recipe)
79
+ agent.run_script(File.expand_path(config[:recipe]))
80
+ end
81
+ if config.has_key?(:directory)
82
+ Dir.glob(File.join(File.expand_path(config[:directory]), '*.rb')).sort.each do |file|
83
+ if File.file?(file)
84
+ agent.run_script(file)
85
+ end
86
+ end
87
+ end
88
+ if config.has_key?(:dryrun) && config[:dryrun]
89
+ puts agent.to_s
90
+ else
91
+ agent.update
92
+ end
data/bin/icpuppet ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # iClassify - A node classification service.
4
+ # Copyright (C) 2007 HJK Solutions and Adam Jacob (<adam@hjksolutions.com>)
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ #
20
+ # Puppet node integration; turns an IClassify node into a puppet node.
21
+ #
22
+
23
+ require 'rubygems'
24
+ require File.dirname(__FILE__) + '/../lib/iclassify-interface'
25
+ require 'optparse'
26
+ require 'highline/import'
27
+
28
+ config = {}
29
+ args = ARGV
30
+ opts = OptionParser.new do |opts|
31
+ opts.banner = "Usage: #{$0} [-s server] hostname"
32
+ opts.on("-s SERVER", "--server", "iClassify Server URL") do |s|
33
+ config[:server] = s
34
+ end
35
+ opts.on("-u user", "--user user", "User to authenticate with, defaults to USER env variable") do |u|
36
+ config[:user] = u
37
+ end
38
+ opts.on("-p passwd", "--passwd passwd", "Password to authenticate with") do |p|
39
+ config[:passwd] = p
40
+ end
41
+ opts.on_tail("-h", "--help", "Show this message") do
42
+ puts opts
43
+ exit
44
+ end
45
+ end
46
+ opts.parse!(args)
47
+
48
+ unless args.length == 1
49
+ puts "You must supply a hostname or fqdn. You supplied: "
50
+ puts args.join(" ")
51
+ puts opts.help
52
+ exit 1
53
+ end
54
+
55
+ unless config[:passwd]
56
+ config[:passwd] = HighLine.ask("Password: ") { |q| q.echo = "*" }
57
+ end
58
+
59
+ unless config[:user] && config[:passwd]
60
+ puts "You must provide a username and password."
61
+ puts opts.help
62
+ exit 1
63
+ end
64
+
65
+
66
+ hostname = args[0]
67
+
68
+ client = IClassify::Client.new(config[:server], config[:user], config[:passwd])
69
+ begin
70
+ results = client.search("hostname:#{hostname} OR fqdn:#{hostname}")
71
+ rescue SocketError
72
+ $stderr.puts("Error: Could not connect to server.")
73
+ exit 1
74
+ end
75
+
76
+ if results.length > 1
77
+ STDERR.puts "Hostname #{hostname} has more than one node definition!"
78
+ exit 10
79
+ end
80
+ # FIXME: This should go away when real support for parents/children shows up.
81
+ base_node = client.search("description:\"Base node\"")
82
+ if base_node.length == 1
83
+ base_node[0].tags.each do |tag|
84
+ results[0].tags << tag unless results[0].tags.detect { |t| t == tag }
85
+ end
86
+ end
87
+ results.each do |node|
88
+ puts node.to_puppet
89
+ end
90
+ exit 0
data/bin/icsearch ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # iClassify - A node classification service.
4
+ # Copyright (C) 2007 HJK Solutions and Adam Jacob (<adam@hjksolutions.com>)
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ #
20
+ # A very simple search utility for iclassify.
21
+ #
22
+
23
+ require 'rubygems'
24
+ require File.dirname(__FILE__) + '/../lib/iclassify-interface'
25
+ require 'optparse'
26
+ require 'highline/import'
27
+
28
+ config = {
29
+ :only => nil,
30
+ :quiet => nil
31
+ }
32
+ opts = OptionParser.new do |opts|
33
+ opts.banner = "Usage: #{$0} (options) query"
34
+ opts.on("-a one,two,three", "--attrib one,two,three", Array, "Attributes to print") do |a|
35
+ config[:attribs] = a
36
+ end
37
+ opts.on("-t", "--tags", "Print tags or not, if attribs specified") do |t|
38
+ config[:tags] = t
39
+ end
40
+ opts.on("-u user", "--user user", "User to authenticate with, defaults to USER env variable") do |u|
41
+ config[:user] = u
42
+ end
43
+ opts.on("-p passwd", "--passwd passwd", "Password to authenticate with") do |p|
44
+ config[:passwd] = p
45
+ end
46
+ opts.on("-s server", "--server server", "iClassify Server URL") do |s|
47
+ config[:server] = s
48
+ end
49
+ opts.on("-o", "--only", "Print only the attributes specified on the command line") do |o|
50
+ config[:only] = o
51
+ end
52
+ opts.on("-q", "--quiet", "Do not print the header") do |q|
53
+ config[:quiet] = q
54
+ end
55
+ opts.on_tail("-h", "--help", "Show this message") do
56
+ puts opts
57
+ exit
58
+ end
59
+ end
60
+ args = ARGV
61
+ opts.parse!(args)
62
+
63
+ if args.length != 1
64
+ puts "You must specify a single query."
65
+ puts opts.help
66
+ exit 1
67
+ end
68
+
69
+ unless config[:passwd]
70
+ config[:passwd] = HighLine.ask("Password: ") { |q| q.echo = "*" }
71
+ end
72
+
73
+ unless config[:user] && config[:passwd]
74
+ puts "You must provide a username and password."
75
+ puts opts.help
76
+ exit 1
77
+ end
78
+
79
+ query = args[0]
80
+
81
+ client = IClassify::Client.new(config[:server], config[:user], config[:passwd])
82
+ begin
83
+ results = client.search(query, config[:attribs] ? config[:attribs] : [])
84
+ rescue SocketError
85
+ $stderr.puts("Error: Could not connect to server.")
86
+ exit 1
87
+ end
88
+
89
+ if config.has_key?(:attribs)
90
+ header = "# "
91
+ if config[:only]
92
+ header += "#{config[:attribs].join(',')}"
93
+ else
94
+ header += "description,uuid,#{config[:attribs].join(',')}"
95
+ end
96
+ header << ",tags" if config.has_key?(:tags)
97
+ puts header unless config[:quiet]
98
+ results.each do |node|
99
+ line = Array.new
100
+ line << "#{node.description}" unless config[:only]
101
+ line << "#{node.uuid}" unless config[:only]
102
+ config[:attribs].each do |attrib|
103
+ na = node.attribs.detect { |a| a[:name] == attrib }
104
+ if na
105
+ line << "#{na[:values].join(':')}"
106
+ else
107
+ line << ""
108
+ end
109
+ end
110
+ line << "\"#{node.tags.join(' ')}\"" if config.has_key?(:tags)
111
+ puts line.join(",")
112
+ end
113
+ else
114
+ results.each do |node|
115
+ puts "#\n# Node #{node.uuid}\n#"
116
+ puts node.to_s
117
+ end
118
+ end
data/bin/icwatcher ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # iClassify - A node classification service.
4
+ # Copyright (C) 2007 HJK Solutions and Adam Jacob (<adam@hjksolutions.com>)
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ #
20
+ # Runs a command if the node has changed in iClassify
21
+ #
22
+
23
+ require 'rubygems'
24
+ require File.dirname(__FILE__) + '/../lib/iclassify-interface'
25
+ require 'optparse'
26
+ require 'tempfile'
27
+ require 'open3'
28
+
29
+ config = {
30
+ :uuidfile => '/etc/icagent/icagent.uuid',
31
+ :tmpfile => File.join(Dir::tmpdir, "icwatcher.digest"),
32
+ }
33
+ verbose = false
34
+
35
+ args = ARGV
36
+ opts = OptionParser.new do |opts|
37
+ opts.banner = "Usage: #{$0} [-s server] -c command"
38
+ opts.on("-s SERVER", "--server", "iClassify Server URL") do |s|
39
+ config[:server] = s
40
+ end
41
+ opts.on("-u UUIDFILE", "--uuidfile UUIDFILE", "Path to the uuid file") do |u|
42
+ config[:uuidfile] = u
43
+ end
44
+ opts.on("-c COMMAND", "--command", "Command to run on changes") do |c|
45
+ config[:command] = c
46
+ end
47
+ opts.on("-t TMPFILE", "--tmpfile", "Where to store the digest between runs") do |t|
48
+ config[:tmpfile] = t
49
+ end
50
+ opts.on("-v", "--verbose", "Print the output of command") do
51
+ verbose = true
52
+ end
53
+ opts.on_tail("-h", "--help", "Show this message") do
54
+ puts opts
55
+ exit
56
+ end
57
+ end
58
+ opts.parse!(args)
59
+
60
+ unless config.has_key?(:command)
61
+ puts "You must supply a command!"
62
+ puts opts.help
63
+ exit 1
64
+ end
65
+
66
+ hostname = args[0]
67
+
68
+ agent = IClassify::Agent.new(config[:uuidfile], config[:server])
69
+ agent.load
70
+ digest = agent.node.digest
71
+
72
+ if FileTest.file?(config[:tmpfile])
73
+ last_digest = File.readlines(config[:tmpfile])
74
+ last_digest[0].chomp!
75
+ if digest == last_digest[0]
76
+ puts "Node has not changed." if verbose
77
+ exit 0
78
+ end
79
+ end
80
+
81
+ output = `#{config[:command]} 2>&1`
82
+ raise "#{config[:command]} failed: #{output}" unless $?.success?
83
+
84
+ if verbose
85
+ puts "Command: #{config[:command]}"
86
+ puts "---- OUTPUT ----"
87
+ puts output
88
+ end
89
+
90
+ File.open(config[:tmpfile], "w") do |tmp|
91
+ tmp.puts digest
92
+ end
93
+
94
+ exit 0
@@ -0,0 +1,60 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{iclassify-interface}
5
+ s.version = "1.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Adam Jacob", "W. Andrew Loe III"]
9
+ s.date = %q{2009-08-21}
10
+ s.email = %q{andrew@andrewloe.com}
11
+ s.executables = ["icagent", "icpuppet", "icsearch", "icwatcher"]
12
+ s.extra_rdoc_files = [
13
+ "README"
14
+ ]
15
+ s.files = [
16
+ ".gitignore",
17
+ "README",
18
+ "Rakefile",
19
+ "VERSION",
20
+ "bin/icagent",
21
+ "bin/icpuppet",
22
+ "bin/icsearch",
23
+ "bin/icwatcher",
24
+ "iclassify-interface.gemspec",
25
+ "lib/iclassify-interface.rb",
26
+ "lib/iclassify-interface/agent.rb",
27
+ "lib/iclassify-interface/client.rb",
28
+ "lib/iclassify-interface/node.rb",
29
+ "recipes/00_facter.rb",
30
+ "recipes/01_default_class.rb",
31
+ "recipes/02_ec2.rb",
32
+ "recipes/02_mongrel_server.rb",
33
+ "recipes/02_puppet_env.rb",
34
+ "recipes/03_mysql_server.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/loe/iclassify-interface}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.4}
40
+ s.summary = %q{Module for interfacing with iclassify.}
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<uuidtools>, [">= 2.0.0"])
48
+ s.add_runtime_dependency(%q<highline>, [">= 0"])
49
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<uuidtools>, [">= 2.0.0"])
52
+ s.add_dependency(%q<highline>, [">= 0"])
53
+ s.add_dependency(%q<builder>, [">= 0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<uuidtools>, [">= 2.0.0"])
57
+ s.add_dependency(%q<highline>, [">= 0"])
58
+ s.add_dependency(%q<builder>, [">= 0"])
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ require 'iclassify-interface/agent'
2
+ require 'iclassify-interface/client'
3
+ require 'iclassify-interface/node'
@@ -0,0 +1,155 @@
1
+ require 'rubygems'
2
+ require 'uuidtools'
3
+
4
+ module IClassify
5
+ class Agent
6
+ attr_accessor :node
7
+ attr_accessor :uuid
8
+ attr_accessor :password
9
+
10
+ #
11
+ # Create a new Agent. Takes a path to a file to either read or drop
12
+ # a UUID, and a server URL.
13
+ #
14
+ def initialize(uuidfile="/etc/icagent/icagent.uuid", server_url="http://localhost:3000")
15
+ @uuid = nil
16
+ @password = nil
17
+ if File.exists?(uuidfile)
18
+ IO.foreach(uuidfile) do |line|
19
+ @uuid, @password = line.chomp.split("!")
20
+ end
21
+ unless @password
22
+ @password = random_password(30)
23
+ write_uuidfile(uuidfile)
24
+ end
25
+ else
26
+ @uuid = UUIDTools::UUID.random_create
27
+ @password = random_password(30)
28
+ write_uuidfile(uuidfile)
29
+ end
30
+ @client = IClassify::Client.new(server_url, @uuid, @password)
31
+ end
32
+
33
+ #
34
+ # Loads data about this node from the iClassify service
35
+ #
36
+ def load
37
+ begin
38
+ @node = @client.get_node(@uuid)
39
+ rescue Net::HTTPServerException => e
40
+ if e.to_s =~ /^404/
41
+ @node = IClassify::Node.new()
42
+ @node.description = "New Node"
43
+ @node.tags << "unclassified"
44
+ @node.password = @password
45
+ @node.uuid = @uuid
46
+ else
47
+ throw(e)
48
+ end
49
+ end
50
+ end
51
+
52
+ #
53
+ # Updates this node in the iClassify service.
54
+ #
55
+ def update
56
+ if @node.description == "New Node"
57
+ hostname = attrib?("hostname")
58
+ hostname ||= "New Node"
59
+ @node.description = hostname
60
+ end
61
+ @client.update_node(@node)
62
+ end
63
+
64
+ #
65
+ # Deletes this node from the iClassify service.
66
+ #
67
+ def delete
68
+ @client.delete_node(@node)
69
+ end
70
+
71
+ #
72
+ # Returns the tag name if this node has that tag.
73
+ #
74
+ def tag?(tag)
75
+ @node.tag?(tag)
76
+ end
77
+
78
+ # Returns the values for this attribute, if it exists for this node. If
79
+ # there is only one, it will return it, if it's an array, you get the
80
+ # array. You have to check!
81
+ def attrib?(attrib)
82
+ @node.attrib?(attrib)
83
+ end
84
+
85
+ # Returns the current node as a string.
86
+ def to_s
87
+ @node.to_s
88
+ end
89
+
90
+ # Returns the value if the given attribute has a given attribute.
91
+ def attrib_has_value?(attrib, value)
92
+ na = @node.attribs.detect { |a| a[:name] == attrib }
93
+ if na
94
+ return na.values.detect { |v| v == value}
95
+ else
96
+ return nil
97
+ end
98
+ end
99
+
100
+ # Add a tag to this node.
101
+ def add_tag(tag)
102
+ load unless @node
103
+ @node.tags << tag
104
+ end
105
+
106
+ # Add an attribute to this node. Requires a name and either a string or
107
+ # array of values.
108
+ #
109
+ # Will be cumulative!
110
+ def add_attrib(name, values)
111
+ load unless @node
112
+ @node.attribs << { :name => name, :values => values.kind_of?(Array) ? values : [ values ] }
113
+ end
114
+
115
+ # Replace the attribute with the given name's values in place.
116
+ # Will add a new attribute if it needs to.
117
+ def replace_attrib(name, values)
118
+ exists = @node.attribs.detect { |a| a[:name] == name }
119
+ if exists
120
+ exists[:values] = values.kind_of?(Array) ? values : [ values ]
121
+ else
122
+ add_attrib(name, values)
123
+ end
124
+ end
125
+
126
+ # Set the description for the node
127
+ def description(value)
128
+ @node.description = value
129
+ end
130
+
131
+ # return the value of @node.description
132
+ def description?()
133
+ @node.description
134
+ end
135
+
136
+ # Run an iclassify script.
137
+ def run_script(scriptfile)
138
+ eval(IO.read(scriptfile))
139
+ end
140
+
141
+ protected
142
+ def random_password(len)
143
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
144
+ newpass = ""
145
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
146
+ newpass
147
+ end
148
+
149
+ def write_uuidfile(uuidfile)
150
+ File.open(uuidfile, "w") do |file|
151
+ file.puts "#{@uuid}!#{@password}"
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,108 @@
1
+ require 'rubygems'
2
+ require 'net/https'
3
+ require 'rexml/document'
4
+ require 'uri'
5
+ require 'yaml'
6
+
7
+ module IClassify
8
+
9
+ class Client
10
+ UUID_REGEX = /^[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}$/
11
+
12
+ def initialize(service_url, username, password)
13
+ service_url = "#{service_url}/rest" unless service_url =~ /rest$/
14
+ @url = URI.parse(service_url)
15
+ @username = username
16
+ @password = password
17
+ end
18
+
19
+ def make_url(method, params)
20
+ params[:appid] = @appid
21
+ super method, params
22
+ end
23
+
24
+ def search(query, attribs=[])
25
+ raise ArgumentError, "Attributes must be given as a list!" unless attribs.kind_of?(Array)
26
+ querystring = "search"
27
+ querystring << "?q=#{URI.escape(query)}"
28
+ querystring << "&a=#{URI.escape(attribs.join(','))}" if attribs.length > 0
29
+ results = get_rest(querystring, "text/yaml")
30
+ node_array = YAML.load(results).collect { |n| IClassify::Node.new(:yaml, n) }
31
+ end
32
+
33
+ def get_node(node_id)
34
+ IClassify::Node.new(:xml, get_rest("nodes/#{node_id}"))
35
+ end
36
+
37
+ def update_node(node)
38
+ if node.node_id
39
+ put_rest("nodes/#{node.node_id}", node.to_xml)
40
+ else
41
+ post_rest("nodes", node.to_xml)
42
+ end
43
+ end
44
+
45
+ def delete_node(node)
46
+ delete_rest("nodes/#{node.node_id}")
47
+ end
48
+
49
+ private
50
+
51
+ def get_rest(path, accept="application/xml")
52
+ url = URI.parse("#{@url}/#{path}")
53
+ run_request(:GET, url, false, accept)
54
+ end
55
+
56
+ def delete_rest(path, accept="application/xml")
57
+ url = URI.parse("#{@url}/#{path}")
58
+ run_request(:DELETE, url, false, accept)
59
+ end
60
+
61
+ def post_rest(path, xml, accept="application/xml")
62
+ url = URI.parse("#{@url}/#{path}")
63
+ run_request(:POST, url, xml, accept)
64
+ end
65
+
66
+ def put_rest(path, xml, accept="application/xml")
67
+ url = URI.parse("#{@url}/#{path}")
68
+ run_request(:PUT, url, xml, accept)
69
+ end
70
+
71
+ def run_request(method, url, data=false, accept="application/xml")
72
+ http = Net::HTTP.new(url.host, url.port)
73
+ if url.scheme == "https"
74
+ http.use_ssl = true
75
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
76
+ end
77
+ http.read_timeout = 60
78
+ headers = {
79
+ 'Accept' => accept,
80
+ 'Content-Type' => accept
81
+ }
82
+ req = nil
83
+ case method
84
+ when :GET
85
+ req_path = "#{url.path}"
86
+ req_path << "?#{url.query}" if url.query
87
+ req = Net::HTTP::Get.new(req_path, headers)
88
+ when :POST
89
+ req = Net::HTTP::Post.new(url.path, headers)
90
+ req.body = data if data
91
+ when :PUT
92
+ req = Net::HTTP::Put.new(url.path, headers)
93
+ req.body = data if data
94
+ when :DELETE
95
+ req = Net::HTTP::Delete.new(url.path, headers)
96
+ end
97
+ req.basic_auth(@username, @password)
98
+ res = http.request(req)
99
+ case res
100
+ when Net::HTTPSuccess
101
+ res.body
102
+ else
103
+ res.error!
104
+ end
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,134 @@
1
+ require 'rubygems'
2
+ require 'rexml/document'
3
+ require 'builder'
4
+ require 'yaml'
5
+ require 'digest/sha1'
6
+
7
+ module IClassify
8
+ class Node
9
+ attr_accessor :tags, :uuid, :description, :notes, :attribs, :node_id, :password
10
+
11
+ def initialize(type=:xml, data=nil)
12
+ from_xml(data) if type == :xml && data
13
+ from_yaml(data) if type == :yaml && data
14
+ @tags ||= Array.new
15
+ @attribs ||= Array.new
16
+ @password = nil
17
+ end
18
+
19
+ def to_xml
20
+ xml = Builder::XmlMarkup.new
21
+ output = xml.node do
22
+ xml.id(@node_id) if @node_id
23
+ xml.uuid(@uuid)
24
+ xml.password(@password) if @password
25
+ xml.description(@description)
26
+ xml.notes(@notes)
27
+ xml.tags do
28
+ @tags.sort.each do |tag|
29
+ xml.tag(tag)
30
+ end
31
+ end
32
+ xml.attribs do
33
+ @attribs.sort{ |a,b| a[:name] <=> b[:name] }.each do |attrib|
34
+ xml.attrib do
35
+ xml.name(attrib[:name])
36
+ xml.values do
37
+ attrib[:values].each do |v|
38
+ xml.value(v)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ output
46
+ end
47
+
48
+ def digest
49
+ Digest::SHA1.hexdigest(to_s())
50
+ end
51
+
52
+ #
53
+ # Returns the tag name if this node has that tag.
54
+ #
55
+ def tag?(tag)
56
+ @tags.detect { |t| t == tag }
57
+ end
58
+
59
+ # Returns the values for this attribute, if it exists for this node. If
60
+ # there is only one, it will return it, if it's an array, you get the
61
+ # array. You have to check!
62
+ def attrib?(attrib)
63
+ na = @attribs.detect { |a| a[:name] == attrib }
64
+ return nil unless na
65
+ if na[:values].length > 1
66
+ return na[:values]
67
+ else
68
+ return na[:values][0]
69
+ end
70
+ end
71
+
72
+ def to_s(tags=nil,attribs=[])
73
+ output = String.new
74
+ output << "uuid: #{@uuid}\n"
75
+ output << "node_id: #{@node_id}\n"
76
+ output << "notes: #{@notes}\n"
77
+ output << "description: #{@description}\n"
78
+ output << "tags: #{@tags.sort.join(' ')}\n"
79
+ output << "attribs:\n"
80
+ @attribs.select{ |attrib| attribs.include?(attrib[:name].to_sym) }.sort{ |a,b| a[:name] <=> b[:name] }.each do |attrib|
81
+ output << " #{attrib[:name]}: #{attrib[:values].join(', ')}\n"
82
+ end
83
+ output
84
+ end
85
+
86
+ def to_puppet
87
+ output = Hash.new
88
+ output["classes"] = @tags
89
+ output["parameters"] = Hash.new
90
+ @attribs.each do |attrib|
91
+ if attrib[:values].length > 1
92
+ output["parameters"][attrib[:name]] = attrib[:values]
93
+ else
94
+ output["parameters"][attrib[:name]] = attrib[:values][0]
95
+ end
96
+ end
97
+ output.to_yaml
98
+ end
99
+
100
+ def from_xml(doc)
101
+ xml = nil
102
+ if doc.kind_of?(REXML::Element)
103
+ xml = doc
104
+ else
105
+ xml = REXML::Document.new(doc)
106
+ end
107
+ @tags = Array.new
108
+ xml.elements.each('//tag') { |t| @tags << t.text }
109
+ @uuid = xml.get_text('//uuid')
110
+ @node_id = xml.get_text('//id')
111
+ @description = xml.get_text('//description')
112
+ @notes = xml.get_text('//notes')
113
+ @attribs = Array.new
114
+ xml.elements.each('//attrib') do |attrib|
115
+ cattrib = Hash.new
116
+ cattrib[:name] = attrib.get_text('name').to_s
117
+ value_array = Array.new
118
+ attrib.elements.each('values/value') { |v| value_array << v.text }
119
+ cattrib[:values] = value_array
120
+ @attribs << cattrib
121
+ end
122
+ end
123
+
124
+ def from_yaml(data)
125
+ @tags = data[:tags].collect { |t| t[:name] }
126
+ @uuid = data[:uuid]
127
+ @description = data[:description]
128
+ @notes = data[:notes]
129
+ @attribs = data[:attribs].delete_if { |x| x[:name] == "text" }
130
+ @node_id = data[:id]
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'facter'
3
+
4
+ Facter.each do |name, value|
5
+ replace_attrib(name, value)
6
+ end
@@ -0,0 +1 @@
1
+ add_tag("base") unless tag?("base")
data/recipes/02_ec2.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'net/http'
2
+
3
+ def get_from_ec2(key = '')
4
+ base_url = "http://instance-data.ec2.internal/latest/meta-data/" + key
5
+ url = URI.parse(base_url)
6
+ req = Net::HTTP::Get.new(url.path)
7
+ res = Net::HTTP.start(url.host, url.port) do |http|
8
+ http.request(req)
9
+ end
10
+
11
+ res.body
12
+ end
13
+
14
+ def get_keys(key = '')
15
+ keys = get_from_ec2(key).split("\n")
16
+
17
+ keys.inject([]) do |accum, k|
18
+ if k =~ /\/$/
19
+ get_keys(key + k).each do |k|
20
+ accum << k
21
+ end
22
+
23
+ accum
24
+ elsif k =~ /=/
25
+ get_keys(key + $` + '/').each do |k|
26
+ accum << k
27
+ end
28
+
29
+ accum
30
+ else
31
+ accum << key + k
32
+
33
+ accum
34
+ end
35
+ end
36
+ end
37
+
38
+ if attrib?('domain') =~ /(\.amazonaws.com|compute-1.internal|ec2.internal)$/
39
+ replace_attrib("ec2", "true")
40
+
41
+ get_keys.each do |key|
42
+ replace_attrib("ec2_#{key.gsub(/-|\//, '_')}", get_from_ec2("#{key}"))
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ mongrel_size = 120
2
+ head_room = 512
3
+
4
+ total_memory = attrib?("memorysize")
5
+ if total_memory
6
+ if total_memory =~ /MB$/
7
+ total_memory.gsub!(/ MB/, '')
8
+ total_memory = total_memory.to_f
9
+ else
10
+ total_memory.gsub!(/ GB/, '')
11
+ total_memory = total_memory.to_f * 1024
12
+ end
13
+ available_memory = total_memory - head_room
14
+ total_mongrels = available_memory / mongrel_size
15
+ add_attrib("mongrel_servers", total_mongrels.to_i) unless attrib?("mongrel_servers")
16
+ add_attrib("mongrel_port_number", 5000) unless attrib?("mongrel_port_number")
17
+ end
@@ -0,0 +1,3 @@
1
+ unless attrib?('puppet_env')
2
+ replace_attrib('puppet_env', ENV['PUPPET_ENV'] || 'production')
3
+ end
@@ -0,0 +1,13 @@
1
+ total_memory = attrib?("memorysize")
2
+ if total_memory
3
+ if total_memory =~ /MB$/
4
+ total_memory.gsub!(/ MB/, '')
5
+ total_memory = total_memory.to_f
6
+ else
7
+ total_memory.gsub!(/ GB/, '')
8
+ total_memory = total_memory.to_f * 1024
9
+ end
10
+ buffer_pool_size = total_memory * 0.75
11
+ buffer_pool_size = buffer_pool_size.to_i
12
+ add_attrib("innodb_buffer_pool_size", buffer_pool_size.to_s + "M") unless attrib?("innodb_buffer_pool_size")
13
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iclassify-interface
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Jacob
8
+ - W. Andrew Loe III
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-08-21 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: uuidtools
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.0.0
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: highline
28
+ type: :runtime
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: builder
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ description:
47
+ email: andrew@andrewloe.com
48
+ executables:
49
+ - icagent
50
+ - icpuppet
51
+ - icsearch
52
+ - icwatcher
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - README
57
+ files:
58
+ - .gitignore
59
+ - README
60
+ - Rakefile
61
+ - VERSION
62
+ - bin/icagent
63
+ - bin/icpuppet
64
+ - bin/icsearch
65
+ - bin/icwatcher
66
+ - iclassify-interface.gemspec
67
+ - lib/iclassify-interface.rb
68
+ - lib/iclassify-interface/agent.rb
69
+ - lib/iclassify-interface/client.rb
70
+ - lib/iclassify-interface/node.rb
71
+ - recipes/00_facter.rb
72
+ - recipes/01_default_class.rb
73
+ - recipes/02_ec2.rb
74
+ - recipes/02_mongrel_server.rb
75
+ - recipes/02_puppet_env.rb
76
+ - recipes/03_mysql_server.rb
77
+ has_rdoc: true
78
+ homepage: http://github.com/loe/iclassify-interface
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options:
83
+ - --charset=UTF-8
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ version:
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ version:
98
+ requirements: []
99
+
100
+ rubyforge_project:
101
+ rubygems_version: 1.3.5
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Module for interfacing with iclassify.
105
+ test_files: []
106
+