iclassify-interface 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+