automate-it 0.9.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.hgignore +10 -0
- data/.loadpath +5 -0
- data/.project +17 -0
- data/CHANGES.txt +314 -0
- data/Hoe.rake +40 -0
- data/Manifest.txt +164 -0
- data/README.txt +40 -0
- data/Rakefile +256 -0
- data/TESTING.txt +57 -0
- data/TODO.txt +50 -0
- data/TUTORIAL.txt +391 -0
- data/automate-it.gemspec +25 -0
- data/bin/ai +3 -0
- data/bin/aifield +75 -0
- data/bin/aissh +93 -0
- data/bin/aitag +134 -0
- data/bin/automateit +133 -0
- data/docs/friendly_errors.txt +50 -0
- data/docs/previews.txt +86 -0
- data/examples/basic/Rakefile +26 -0
- data/examples/basic/config/automateit_env.rb +16 -0
- data/examples/basic/config/fields.yml +3 -0
- data/examples/basic/config/tags.yml +7 -0
- data/examples/basic/dist/README.txt +9 -0
- data/examples/basic/dist/myapp_server.erb +30 -0
- data/examples/basic/install.log +15 -0
- data/examples/basic/lib/README.txt +10 -0
- data/examples/basic/recipes/README.txt +4 -0
- data/examples/basic/recipes/install.rb +61 -0
- data/examples/basic/recipes/uninstall.rb +6 -0
- data/gpl.txt +674 -0
- data/helpers/cpan_wrapper.pl +220 -0
- data/helpers/which.cmd +7 -0
- data/lib/automateit.rb +55 -0
- data/lib/automateit/account_manager.rb +114 -0
- data/lib/automateit/account_manager/base.rb +138 -0
- data/lib/automateit/account_manager/etc.rb +128 -0
- data/lib/automateit/account_manager/nscd.rb +33 -0
- data/lib/automateit/account_manager/passwd_expect.rb +40 -0
- data/lib/automateit/account_manager/passwd_pty.rb +69 -0
- data/lib/automateit/account_manager/posix.rb +138 -0
- data/lib/automateit/address_manager.rb +88 -0
- data/lib/automateit/address_manager/base.rb +171 -0
- data/lib/automateit/address_manager/bsd.rb +28 -0
- data/lib/automateit/address_manager/freebsd.rb +59 -0
- data/lib/automateit/address_manager/linux.rb +42 -0
- data/lib/automateit/address_manager/openbsd.rb +66 -0
- data/lib/automateit/address_manager/portable.rb +37 -0
- data/lib/automateit/address_manager/sunos.rb +34 -0
- data/lib/automateit/cli.rb +85 -0
- data/lib/automateit/common.rb +65 -0
- data/lib/automateit/constants.rb +35 -0
- data/lib/automateit/download_manager.rb +48 -0
- data/lib/automateit/edit_manager.rb +321 -0
- data/lib/automateit/error.rb +10 -0
- data/lib/automateit/field_manager.rb +103 -0
- data/lib/automateit/interpreter.rb +631 -0
- data/lib/automateit/package_manager.rb +257 -0
- data/lib/automateit/package_manager/apt.rb +27 -0
- data/lib/automateit/package_manager/cpan.rb +101 -0
- data/lib/automateit/package_manager/dpkg.rb +54 -0
- data/lib/automateit/package_manager/egg.rb +64 -0
- data/lib/automateit/package_manager/gem.rb +201 -0
- data/lib/automateit/package_manager/pear.rb +95 -0
- data/lib/automateit/package_manager/pecl.rb +80 -0
- data/lib/automateit/package_manager/portage.rb +69 -0
- data/lib/automateit/package_manager/yum.rb +65 -0
- data/lib/automateit/platform_manager.rb +49 -0
- data/lib/automateit/platform_manager/darwin.rb +30 -0
- data/lib/automateit/platform_manager/debian.rb +26 -0
- data/lib/automateit/platform_manager/freebsd.rb +29 -0
- data/lib/automateit/platform_manager/gentoo.rb +26 -0
- data/lib/automateit/platform_manager/lsb.rb +44 -0
- data/lib/automateit/platform_manager/openbsd.rb +28 -0
- data/lib/automateit/platform_manager/struct.rb +80 -0
- data/lib/automateit/platform_manager/sunos.rb +39 -0
- data/lib/automateit/platform_manager/uname.rb +29 -0
- data/lib/automateit/platform_manager/windows.rb +40 -0
- data/lib/automateit/plugin.rb +7 -0
- data/lib/automateit/plugin/base.rb +32 -0
- data/lib/automateit/plugin/driver.rb +256 -0
- data/lib/automateit/plugin/manager.rb +224 -0
- data/lib/automateit/project.rb +493 -0
- data/lib/automateit/root.rb +17 -0
- data/lib/automateit/service_manager.rb +93 -0
- data/lib/automateit/service_manager/chkconfig.rb +39 -0
- data/lib/automateit/service_manager/rc_update.rb +37 -0
- data/lib/automateit/service_manager/sysv.rb +139 -0
- data/lib/automateit/service_manager/update_rcd.rb +35 -0
- data/lib/automateit/shell_manager.rb +316 -0
- data/lib/automateit/shell_manager/base_link.rb +67 -0
- data/lib/automateit/shell_manager/link.rb +24 -0
- data/lib/automateit/shell_manager/portable.rb +523 -0
- data/lib/automateit/shell_manager/symlink.rb +32 -0
- data/lib/automateit/shell_manager/which_base.rb +30 -0
- data/lib/automateit/shell_manager/which_unix.rb +16 -0
- data/lib/automateit/shell_manager/which_windows.rb +20 -0
- data/lib/automateit/tag_manager.rb +127 -0
- data/lib/automateit/tag_manager/struct.rb +121 -0
- data/lib/automateit/tag_manager/tag_parser.rb +93 -0
- data/lib/automateit/tag_manager/yaml.rb +29 -0
- data/lib/automateit/template_manager.rb +56 -0
- data/lib/automateit/template_manager/base.rb +181 -0
- data/lib/automateit/template_manager/erb.rb +17 -0
- data/lib/ext/metaclass.rb +17 -0
- data/lib/ext/object.rb +18 -0
- data/lib/ext/shell_escape.rb +7 -0
- data/lib/hashcache.rb +22 -0
- data/lib/helpful_erb.rb +63 -0
- data/lib/inactive_support.rb +53 -0
- data/lib/inactive_support/basic_object.rb +6 -0
- data/lib/inactive_support/clean_logger.rb +127 -0
- data/lib/inactive_support/core_ext/array/extract_options.rb +19 -0
- data/lib/inactive_support/core_ext/blank.rb +50 -0
- data/lib/inactive_support/core_ext/class/attribute_accessors.rb +48 -0
- data/lib/inactive_support/core_ext/class/inheritable_attributes.rb +140 -0
- data/lib/inactive_support/core_ext/enumerable.rb +63 -0
- data/lib/inactive_support/core_ext/hash/keys.rb +54 -0
- data/lib/inactive_support/core_ext/module/aliasing.rb +70 -0
- data/lib/inactive_support/core_ext/numeric/time.rb +91 -0
- data/lib/inactive_support/core_ext/string/inflections.rb +153 -0
- data/lib/inactive_support/core_ext/symbol.rb +14 -0
- data/lib/inactive_support/core_ext/time/conversions.rb +96 -0
- data/lib/inactive_support/duration.rb +96 -0
- data/lib/inactive_support/inflections.rb +53 -0
- data/lib/inactive_support/inflector.rb +282 -0
- data/lib/nested_error.rb +33 -0
- data/lib/nitpick.rb +33 -0
- data/lib/queued_logger.rb +68 -0
- data/lib/tempster.rb +250 -0
- data/misc/index_gem_repository.rb +304 -0
- data/misc/setup_egg.rb +12 -0
- data/misc/setup_gem_dependencies.sh +6 -0
- data/misc/setup_rubygems.sh +21 -0
- metadata +279 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
# == ShellManager::Symlink
|
2
|
+
#
|
3
|
+
# A ShellManager driver providing access to the symbolic link +ln_s+ command
|
4
|
+
# found on Unix-like systems.
|
5
|
+
class AutomateIt::ShellManager::Symlink < AutomateIt::ShellManager::BaseLink
|
6
|
+
depends_on \
|
7
|
+
:libraries => %w(pathname),
|
8
|
+
:callbacks => [lambda{
|
9
|
+
# JRuby can make symlinks but can't read them.
|
10
|
+
RUBY_PLATFORM !~ /java|mswin/i and File.respond_to?(:symlink)
|
11
|
+
}]
|
12
|
+
|
13
|
+
def suitability(method, *args) # :nodoc:
|
14
|
+
# Level must be higher than Portable
|
15
|
+
return available? ? 2 : 0
|
16
|
+
end
|
17
|
+
|
18
|
+
# See ShellManager#provides_symlink?
|
19
|
+
def provides_symlink?
|
20
|
+
available? ? true : false
|
21
|
+
end
|
22
|
+
|
23
|
+
# See ShellManager#ln_s
|
24
|
+
def ln_s(sources, target, opts={})
|
25
|
+
_ln(sources, target, {:symbolic => true}.merge(opts))
|
26
|
+
end
|
27
|
+
|
28
|
+
# See ShellManager#ln_sf
|
29
|
+
def ln_sf(sources, target, opts={})
|
30
|
+
_ln(sources, target, {:symbolic => true, :force => true}.merge(opts))
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# == ShellManager::WhichBase
|
2
|
+
#
|
3
|
+
# Provides abstract helper methods for other drivers implementing the +which+.
|
4
|
+
class AutomateIt::ShellManager::WhichBase < AutomateIt::ShellManager::BaseDriver
|
5
|
+
abstract_driver
|
6
|
+
|
7
|
+
def suitability(method, *args) # :nodoc:
|
8
|
+
# Level must be higher than Portable
|
9
|
+
return available? ? 2 : 0
|
10
|
+
end
|
11
|
+
|
12
|
+
# See ShellManager#which!
|
13
|
+
def which!(command)
|
14
|
+
result = which(command)
|
15
|
+
if result.nil?
|
16
|
+
raise ArgumentError.new("command not found: #{command}")
|
17
|
+
else
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def _which_helper(&block)
|
25
|
+
data = block.call
|
26
|
+
data.strip! if data
|
27
|
+
return (! data.blank? && File.exists?(data)) ? data : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# == ShellManager::WhichUnix
|
2
|
+
#
|
3
|
+
# A ShellManager driver providing access to the +which+ command found on
|
4
|
+
# Unix-like systems.
|
5
|
+
class AutomateIt::ShellManager::WhichUnix < AutomateIt::ShellManager::WhichBase
|
6
|
+
depends_on :programs => %w(which)
|
7
|
+
|
8
|
+
# Inherits WhichBase#suitability
|
9
|
+
|
10
|
+
# See ShellManager#which
|
11
|
+
def which(command)
|
12
|
+
_which_helper do
|
13
|
+
`which #{command} 2>&1`
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# == ShellManager::WhichWindows
|
2
|
+
#
|
3
|
+
# A ShellManager driver providing access to +which+ command by faking it on
|
4
|
+
# Windows systems.
|
5
|
+
class ::AutomateIt::ShellManager::WhichWindows < ::AutomateIt::ShellManager::WhichBase
|
6
|
+
WHICH_HELPER = File.join(::AutomateIt::Constants::HELPERS_DIR, "which.cmd")
|
7
|
+
|
8
|
+
# FIXME how to detect windows through Java?
|
9
|
+
depends_on :callbacks => lambda { RUBY_PLATFORM =~ /mswin/i }
|
10
|
+
|
11
|
+
# Inherits WhichBase#suitability
|
12
|
+
|
13
|
+
# See ShellManager#which
|
14
|
+
def which(command)
|
15
|
+
_which_helper do
|
16
|
+
data = `#{WHICH_HELPER} #{command} 2>&1`
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# == TagManager
|
2
|
+
#
|
3
|
+
# The TagManager provides a way of querying tags. Tags are keywords
|
4
|
+
# associated with a specific hostname or group. These are useful for grouping
|
5
|
+
# together hosts and defining common behavior for them.
|
6
|
+
#
|
7
|
+
# === Basics
|
8
|
+
#
|
9
|
+
# The tags are typically stored in a Project's <tt>config/tags.yml</tt> file.
|
10
|
+
#
|
11
|
+
# For example, consider this <tt>config/tags.yml</tt> file:
|
12
|
+
# desktops:
|
13
|
+
# - satori
|
14
|
+
# - sunyata
|
15
|
+
# - michiru
|
16
|
+
# notebooks:
|
17
|
+
# - rheya
|
18
|
+
# - avijja
|
19
|
+
#
|
20
|
+
# With the above file, if we're on the host called "satori", we can query the
|
21
|
+
# fields like this:
|
22
|
+
# tags # => ["satori", "desktops", "localhost", ...]
|
23
|
+
#
|
24
|
+
# tagged?("desktops") # => true
|
25
|
+
# tagged?("notebooks") # => false
|
26
|
+
# tagged?(:satori) # => true
|
27
|
+
# tagged?("satori") # => true
|
28
|
+
# tagged?("satori || desktops") # => true
|
29
|
+
# tagged?("(satori || desktops) && !notebooks") # => true
|
30
|
+
#
|
31
|
+
# === Traits
|
32
|
+
#
|
33
|
+
# Your system may also automatically add tags that describe your system's
|
34
|
+
# traits, such as the name of the operating system, distribution release,
|
35
|
+
# hardware architecture, hostnames, IP addresses, etc.
|
36
|
+
#
|
37
|
+
# For example, here is a full set of tags for a system:
|
38
|
+
#
|
39
|
+
# ai> pp tags.sort # Pretty print the tags in sorted order
|
40
|
+
# ["10.0.0.6", # IPv4 addresses
|
41
|
+
# "127.0.0.1", # ...
|
42
|
+
# "192.168.130.1", # ...
|
43
|
+
# "::1/128", # IPv6 addresses
|
44
|
+
# "fe80::250:56ff:fec0:8/64", # ...
|
45
|
+
# "fe80::250:8dff:fe95:8fe9/64", # ...
|
46
|
+
# "i686", # Hardware architecture
|
47
|
+
# "linux", # OS
|
48
|
+
# "linux_i686", # OS and architecture
|
49
|
+
# "localhost", # Variants of hostname
|
50
|
+
# "localhost.localdomain", # ...
|
51
|
+
# "michiru", # ...
|
52
|
+
# "michiru.koshevoy", # ...
|
53
|
+
# "michiru.koshevoy.net", # ...
|
54
|
+
# "myapp_servers", # User defined tags
|
55
|
+
# "rails_servers", # ...
|
56
|
+
# "ubuntu", # OS distribution name
|
57
|
+
# "ubuntu_6.06"] # OS distribution name and release version
|
58
|
+
#
|
59
|
+
# To execute code only on an Ubuntu system:
|
60
|
+
#
|
61
|
+
# if tagged?("ubuntu")
|
62
|
+
# # Code will only be run on Ubuntu systems
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# These additional tags are retrieved from the PlatformManager and
|
66
|
+
# AddressManager. If your platform does not provide drivers for these, you will
|
67
|
+
# not get these tags. If you're on an unsupported platform and do not want to
|
68
|
+
# write drivers, you can work around this by manually declaring the missing
|
69
|
+
# tags in <tt>config/tags.yml</tt> on a host-by-host basis.
|
70
|
+
#
|
71
|
+
# === Inclusion and negation
|
72
|
+
#
|
73
|
+
# You can include and negate tags declaratively by giving "@" and "!" prefixes
|
74
|
+
# to arguments.
|
75
|
+
#
|
76
|
+
# For example, consider this <tt>config/tags.yml</tt> file:
|
77
|
+
#
|
78
|
+
# apache_servers:
|
79
|
+
# - kurou
|
80
|
+
# - shirou
|
81
|
+
# apache_servers_except_kurou:
|
82
|
+
# - @apache_servers
|
83
|
+
# - !kurou
|
84
|
+
#
|
85
|
+
# This will produce the following results:
|
86
|
+
#
|
87
|
+
# ai> hosts_tagged_with("apache_servers")
|
88
|
+
# => ["kurou", "shirou"]
|
89
|
+
# ai> hosts_tagged_with("apache_servers_except_kurou")
|
90
|
+
# => ["shirou"]
|
91
|
+
class AutomateIt::TagManager < AutomateIt::Plugin::Manager
|
92
|
+
alias_methods :hosts_tagged_with, :tags, :tagged?, :tags_for
|
93
|
+
|
94
|
+
# Return a list of hosts that match the query. See #tagged? for information
|
95
|
+
# on query syntax.
|
96
|
+
def hosts_tagged_with(query) dispatch(query) end
|
97
|
+
|
98
|
+
# Return a list of tags for this host.
|
99
|
+
def tags() dispatch() end
|
100
|
+
|
101
|
+
# Is this host tagged with the +query+?
|
102
|
+
#
|
103
|
+
# Examples:
|
104
|
+
# tags # => ["localhost", "foo", "bar", ...]
|
105
|
+
#
|
106
|
+
# tagged?(:localhost) # => true
|
107
|
+
# tagged?("localhost") # => true
|
108
|
+
# tagged?("localhost && foo") # => true
|
109
|
+
# tagged?("localhost || foo") # => true
|
110
|
+
# tagged?("!foo") # => false
|
111
|
+
# tagged?("(localhost || foo) && bar") # => true
|
112
|
+
def tagged?(query, hostname=nil) dispatch(query, hostname) end
|
113
|
+
|
114
|
+
# Return a list of tags for the host.
|
115
|
+
def tags_for(hostname) dispatch(hostname) end
|
116
|
+
end
|
117
|
+
|
118
|
+
# == TagManager::BaseDriver
|
119
|
+
#
|
120
|
+
# Base class for all TagManager drivers.
|
121
|
+
class AutomateIt::TagManager::BaseDriver < AutomateIt::Plugin::Driver
|
122
|
+
end
|
123
|
+
|
124
|
+
# Drivers
|
125
|
+
require 'automateit/tag_manager/tag_parser'
|
126
|
+
require 'automateit/tag_manager/struct'
|
127
|
+
require 'automateit/tag_manager/yaml'
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# == TagManager::Struct
|
2
|
+
#
|
3
|
+
# A TagManager driver for querying a data structure. It's not useful on its
|
4
|
+
# own, but can be subclassed by other drivers that actually load tags.
|
5
|
+
class AutomateIt::TagManager::Struct < AutomateIt::TagManager::BaseDriver
|
6
|
+
depends_on :nothing
|
7
|
+
|
8
|
+
def suitability(method, *args) # :nodoc:
|
9
|
+
return 1
|
10
|
+
end
|
11
|
+
|
12
|
+
# Options:
|
13
|
+
# * :struct -- Hash to use for queries.
|
14
|
+
def setup(opts={})
|
15
|
+
super(opts)
|
16
|
+
@struct ||= {}
|
17
|
+
@tags ||= Set.new
|
18
|
+
@struct_updated = false
|
19
|
+
@our_hostname_tags ||= Set.new
|
20
|
+
@our_platform_tags ||= Set.new
|
21
|
+
|
22
|
+
if opts[:struct]
|
23
|
+
@struct_updated = true
|
24
|
+
@struct.merge!(AutomateIt::TagManager::TagParser.expand(opts[:struct]))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return tags, populate them if necessary.
|
29
|
+
def tags
|
30
|
+
if @our_hostname_tags.empty?
|
31
|
+
@struct_updated = true
|
32
|
+
|
33
|
+
begin
|
34
|
+
@our_hostname_tags.merge(interpreter.address_manager.hostnames) # SLOW 0.4s
|
35
|
+
@our_hostname_tags.merge(interpreter.address_manager.addresses)
|
36
|
+
rescue NotImplementedError => e
|
37
|
+
log.debug("Can't find AddressManager for this platform: #{e}")
|
38
|
+
end
|
39
|
+
|
40
|
+
@tags.merge(@our_hostname_tags)
|
41
|
+
end
|
42
|
+
|
43
|
+
if @our_platform_tags.empty?
|
44
|
+
@struct_updated = true
|
45
|
+
|
46
|
+
begin
|
47
|
+
@our_platform_tags = interpreter.platform_manager.tags
|
48
|
+
@tags.merge(@our_platform_tags)
|
49
|
+
rescue NotImplementedError => e
|
50
|
+
log.debug("Can't find PlatformManager for this platform: #{e}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if @struct_updated
|
55
|
+
@tags.merge(tags_for(@our_hostname_tags))
|
56
|
+
@struct_updated = false
|
57
|
+
end
|
58
|
+
|
59
|
+
@tags
|
60
|
+
end
|
61
|
+
|
62
|
+
# See TagManager#hosts_tagged_with
|
63
|
+
def hosts_tagged_with(query)
|
64
|
+
hosts = @struct.values.flatten.uniq
|
65
|
+
return hosts.select{|hostname| tagged?(query, hostname)}
|
66
|
+
end
|
67
|
+
|
68
|
+
TAG_NEGATION = %r{!?}
|
69
|
+
TAG_WORD = %r{[\w\.\-]+}
|
70
|
+
TAG_TOKENIZER = %r{\!|\(|\)|\&{1,2}|\|{1,2}|#{TAG_NEGATION}#{TAG_WORD}}
|
71
|
+
|
72
|
+
# See TagManager#tagged?
|
73
|
+
def tagged?(query, hostname=nil)
|
74
|
+
query = query.to_s
|
75
|
+
selected_tags = hostname ? tags_for(hostname) : tags
|
76
|
+
# XXX Tokenizer discards unknown characters, which may hide errors in the query
|
77
|
+
tokens = query.scan(TAG_TOKENIZER)
|
78
|
+
if tokens.size > 1
|
79
|
+
booleans = tokens.map do |token|
|
80
|
+
if matches = token.match(/^(#{TAG_NEGATION})(#{TAG_WORD})$/)
|
81
|
+
selected_tags.include?(matches[2]) && matches[1].empty?
|
82
|
+
else
|
83
|
+
token
|
84
|
+
end
|
85
|
+
end
|
86
|
+
code = booleans.join(" ")
|
87
|
+
|
88
|
+
begin
|
89
|
+
return eval(code) # XXX What could possibly go wrong?
|
90
|
+
rescue Exception => e
|
91
|
+
raise ArgumentError.new("Invalid query -- #{query}")
|
92
|
+
end
|
93
|
+
else
|
94
|
+
return selected_tags.include?(query)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# See TagManager#tags_for
|
99
|
+
def tags_for(hostnames)
|
100
|
+
hostnames = \
|
101
|
+
case hostnames
|
102
|
+
when String
|
103
|
+
interpreter.address_manager.hostnames_for(hostnames)
|
104
|
+
when Array, Set
|
105
|
+
hostnames.inject(Set.new) do |sum, hostname|
|
106
|
+
sum.merge(interpreter.address_manager.hostnames_for(hostname)); sum
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise TypeError.new("invalid hostnames argument type: #{hostnames.class}")
|
110
|
+
end
|
111
|
+
result = @struct.inject(Set.new) do |sum, role_and_members|
|
112
|
+
role, members = role_and_members
|
113
|
+
members_aliases = members.inject(Set.new) do |aliases, member|
|
114
|
+
aliases.merge(interpreter.address_manager.hostnames_for(member)); aliases
|
115
|
+
end.to_a
|
116
|
+
sum.add(role) unless (hostnames & members_aliases).empty?
|
117
|
+
sum
|
118
|
+
end
|
119
|
+
return result.to_a.sort
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# == TagManager::TagParser
|
2
|
+
#
|
3
|
+
# Helper class for parsing tags. Not useful for users -- for internal use only.
|
4
|
+
class AutomateIt::TagManager::TagParser
|
5
|
+
include Nitpick
|
6
|
+
|
7
|
+
attr_accessor :struct
|
8
|
+
|
9
|
+
# Create a parser for the +struct+, a hash of tag keys to values with arrays of items.
|
10
|
+
def initialize(struct)
|
11
|
+
self.struct = struct
|
12
|
+
normalize!
|
13
|
+
end
|
14
|
+
|
15
|
+
# Normalize a block of text to replace shortcut symbols that cause YAML to choke.
|
16
|
+
def self.normalize(text)
|
17
|
+
return text \
|
18
|
+
.gsub(/^(\s*-\s+)(!@)/, '\1EXCLUDE_TAG ') \
|
19
|
+
.gsub(/^(\s*-\s+)(!)/, '\1EXCLUDE_HOST ') \
|
20
|
+
.gsub(/^(\s*-\s+)(@)/, '\1INCLUDE_TAG ')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Normalize the contents of the internal struct.
|
24
|
+
def normalize!
|
25
|
+
for tag, items in struct
|
26
|
+
next unless items
|
27
|
+
for item in items
|
28
|
+
next unless item
|
29
|
+
item.gsub!(/^(\!@|\^@)\s*/, 'EXCLUDE_TAG ')
|
30
|
+
item.gsub!(/^(\!|\^)\s*/, 'EXCLUDE_HOST ')
|
31
|
+
item.gsub!(/^(@)\s*/, 'INCLUDE_TAG ')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
HOSTS_FOR_VALUE = /(.+?)/
|
37
|
+
HOSTS_FOR_INCLUDE_TAG_RE = /^INCLUDE_TAG #{HOSTS_FOR_VALUE}$/
|
38
|
+
HOSTS_FOR_EXCLUDE_TAG_RE = /^EXCLUDE_TAG #{HOSTS_FOR_VALUE}$/
|
39
|
+
HOSTS_FOR_EXCLUDE_HOST_RE = /^EXCLUDE_HOST #{HOSTS_FOR_VALUE}$/
|
40
|
+
|
41
|
+
# Return array of hosts for the +tag+.
|
42
|
+
def hosts_for(tag)
|
43
|
+
raise IndexError.new("Unknown tag - #{tag}") unless struct.has_key?(tag)
|
44
|
+
return [] if struct[tag].nil? # Tag has no leaves
|
45
|
+
|
46
|
+
nitpick "\nAA %s" % tag
|
47
|
+
hosts = Set.new
|
48
|
+
for item in struct[tag]
|
49
|
+
case item
|
50
|
+
when HOSTS_FOR_INCLUDE_TAG_RE
|
51
|
+
nitpick "+g %s" % $1
|
52
|
+
hosts.merge(hosts_for($1))
|
53
|
+
when HOSTS_FOR_EXCLUDE_TAG_RE
|
54
|
+
nitpick "-g %s" % $1
|
55
|
+
hosts.subtract(hosts_for($1))
|
56
|
+
when HOSTS_FOR_EXCLUDE_HOST_RE
|
57
|
+
nitpick "-h %s" % $1
|
58
|
+
hosts.delete($1)
|
59
|
+
else
|
60
|
+
nitpick "+h %s" % item
|
61
|
+
hosts << item
|
62
|
+
end
|
63
|
+
end
|
64
|
+
result = hosts.to_a
|
65
|
+
nitpick "ZZ %s for %s" % [result.inspect, tag]
|
66
|
+
return result
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return array of tags.
|
70
|
+
def tags
|
71
|
+
return struct.keys
|
72
|
+
end
|
73
|
+
|
74
|
+
# Expand the include/exclude/group rules and return a struct with only the
|
75
|
+
# hosts these rules produce.
|
76
|
+
def expand
|
77
|
+
result = {}
|
78
|
+
for tag in tags
|
79
|
+
result[tag] = hosts_for(tag)
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
# Replace the internal struct with an expanded version, see #expand.
|
85
|
+
def expand!
|
86
|
+
struct.replace(expand)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Expand the +struct+.
|
90
|
+
def self.expand(struct)
|
91
|
+
self.new(struct).expand
|
92
|
+
end
|
93
|
+
end
|