shu-san-scripts 0.0.2
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/.document +5 -0
- data/.rvmrc +27 -0
- data/Gemfile +34 -0
- data/Gemfile.lock +32 -0
- data/HISTORY +29 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +55 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/store +50 -0
- data/lib/SANStore.rb +24 -0
- data/lib/SANStore/cli.rb +29 -0
- data/lib/SANStore/cli/base.rb +102 -0
- data/lib/SANStore/cli/commands.rb +30 -0
- data/lib/SANStore/cli/commands/help.rb +106 -0
- data/lib/SANStore/cli/commands/list_vols.rb +87 -0
- data/lib/SANStore/cli/commands/new_vol.rb +103 -0
- data/lib/SANStore/cli/logger.rb +88 -0
- data/lib/SANStore/cri.rb +12 -0
- data/lib/SANStore/cri/base.rb +153 -0
- data/lib/SANStore/cri/command.rb +104 -0
- data/lib/SANStore/cri/core_ext.rb +8 -0
- data/lib/SANStore/cri/core_ext/string.rb +41 -0
- data/lib/SANStore/cri/option_parser.rb +186 -0
- data/shu-san-scripts.gemspec +85 -0
- data/test/helper.rb +18 -0
- data/test/test_shu-san-scripts.rb +7 -0
- metadata +199 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
module Cri
|
2
|
+
|
3
|
+
# Cri::Command represents a command that can be executed on the commandline.
|
4
|
+
# It is an abstract superclass for all commands.
|
5
|
+
class Command
|
6
|
+
|
7
|
+
attr_accessor :base
|
8
|
+
|
9
|
+
# Returns a string containing the name of thi command. Subclasses must
|
10
|
+
# implement this method.
|
11
|
+
def name
|
12
|
+
raise NotImplementedError.new("Command subclasses should override #name")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns an array of strings containing the aliases for this command.
|
16
|
+
# Subclasses must implement this method.
|
17
|
+
def aliases
|
18
|
+
raise NotImplementedError.new("Command subclasses should override #aliases")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a string containing this command's short description, which
|
22
|
+
# should not be longer than 50 characters. Subclasses must implement this
|
23
|
+
# method.
|
24
|
+
def short_desc
|
25
|
+
raise NotImplementedError.new("Command subclasses should override #short_desc")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a string containing this command's complete description, which
|
29
|
+
# should explain what this command does and how it works in detail.
|
30
|
+
# Subclasses must implement this method.
|
31
|
+
def long_desc
|
32
|
+
raise NotImplementedError.new("Command subclasses should override #long_desc")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a string containing this command's usage. Subclasses must
|
36
|
+
# implement this method.
|
37
|
+
def usage
|
38
|
+
raise NotImplementedError.new("Command subclasses should override #usage")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns an array containing this command's option definitions. See the
|
42
|
+
# documentation for Cri::OptionParser for details on what option
|
43
|
+
# definitions look like. Subclasses may implement this method if the
|
44
|
+
# command has options.
|
45
|
+
def option_definitions
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Executes the command. Subclasses must implement this method
|
50
|
+
# (obviously... what's the point of a command that can't be run?).
|
51
|
+
#
|
52
|
+
# +options+:: A hash containing the parsed commandline options. For
|
53
|
+
# example, '--foo=bar' will be converted into { :foo => 'bar'
|
54
|
+
# }. See the Cri::OptionParser documentation for details.
|
55
|
+
#
|
56
|
+
# +arguments+:: An array of strings representing the commandline arguments
|
57
|
+
# given to this command.
|
58
|
+
def run(options, arguments)
|
59
|
+
raise NotImplementedError.new("Command subclasses should override #run")
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the help text for this command.
|
63
|
+
def help
|
64
|
+
text = ''
|
65
|
+
|
66
|
+
# Append usage
|
67
|
+
text << usage + "\n"
|
68
|
+
|
69
|
+
# Append aliases
|
70
|
+
unless aliases.empty?
|
71
|
+
text << "\n"
|
72
|
+
text << "aliases: #{aliases.join(' ')}\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Append short description
|
76
|
+
text << "\n"
|
77
|
+
text << short_desc + "\n"
|
78
|
+
|
79
|
+
# Append long description
|
80
|
+
text << "\n"
|
81
|
+
text << long_desc.wrap_and_indent(78, 4) + "\n"
|
82
|
+
|
83
|
+
# Append options
|
84
|
+
unless option_definitions.empty?
|
85
|
+
text << "\n"
|
86
|
+
text << "options:\n"
|
87
|
+
text << "\n"
|
88
|
+
option_definitions.sort { |x,y| x[:long] <=> y[:long] }.each do |opt_def|
|
89
|
+
text << sprintf(" -%1s --%-10s %s\n", opt_def[:short], opt_def[:long], opt_def[:desc])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return text
|
94
|
+
text
|
95
|
+
end
|
96
|
+
|
97
|
+
# Compares this command's name to the other given command's name.
|
98
|
+
def <=>(other)
|
99
|
+
self.name <=> other.name
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Cri::CoreExtensions
|
2
|
+
|
3
|
+
module String
|
4
|
+
|
5
|
+
# Word-wraps and indents the string.
|
6
|
+
#
|
7
|
+
# +width+:: The maximal width of each line. This also includes indentation,
|
8
|
+
# i.e. the actual maximal width of the text is width-indentation.
|
9
|
+
#
|
10
|
+
# +indentation+:: The number of spaces to indent each wrapped line.
|
11
|
+
def wrap_and_indent(width, indentation)
|
12
|
+
# Split into paragraphs
|
13
|
+
paragraphs = self.split("\n").map { |p| p.strip }.reject { |p| p == '' }
|
14
|
+
|
15
|
+
# Wrap and indent each paragraph
|
16
|
+
paragraphs.map do |paragraph|
|
17
|
+
# Initialize
|
18
|
+
lines = []
|
19
|
+
line = ''
|
20
|
+
|
21
|
+
# Split into words
|
22
|
+
paragraph.split(/\s/).each do |word|
|
23
|
+
# Begin new line if it's too long
|
24
|
+
if (line + ' ' + word).length >= width
|
25
|
+
lines << line
|
26
|
+
line = ''
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add word to line
|
30
|
+
line += (line == '' ? '' : ' ' ) + word
|
31
|
+
end
|
32
|
+
lines << line
|
33
|
+
|
34
|
+
# Join lines
|
35
|
+
lines.map { |l| ' '*indentation + l }.join("\n")
|
36
|
+
end.join("\n\n")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module Cri
|
2
|
+
|
3
|
+
# Cri::OptionParser is used for parsing commandline options.
|
4
|
+
class OptionParser
|
5
|
+
|
6
|
+
# Error that will be raised when an unknown option is encountered.
|
7
|
+
class IllegalOptionError < RuntimeError ; end
|
8
|
+
|
9
|
+
# Error that will be raised when an option without argument is
|
10
|
+
# encountered.
|
11
|
+
class OptionRequiresAnArgumentError < RuntimeError ; end
|
12
|
+
|
13
|
+
# Parses the commandline arguments in +arguments_and_options+, using the
|
14
|
+
# commandline option definitions in +definitions+.
|
15
|
+
#
|
16
|
+
# +arguments_and_options+ is an array of commandline arguments and
|
17
|
+
# options. This will usually be +ARGV+.
|
18
|
+
#
|
19
|
+
# +definitions+ contains a list of hashes defining which options are
|
20
|
+
# allowed and how they will be handled. Such a hash has three keys:
|
21
|
+
#
|
22
|
+
# :short:: The short name of the option, e.g. +a+. Do not include the '-'
|
23
|
+
# prefix.
|
24
|
+
#
|
25
|
+
# :long:: The long name of the option, e.g. +all+. Do not include the '--'
|
26
|
+
# prefix.
|
27
|
+
#
|
28
|
+
# :argument:: Whether this option's argument is required (:required),
|
29
|
+
# optional (:optional) or forbidden (:forbidden).
|
30
|
+
#
|
31
|
+
# A sample array of definition hashes could look like this:
|
32
|
+
#
|
33
|
+
# [
|
34
|
+
# { :short => 'a', :long => 'all', :argument => :forbidden },
|
35
|
+
# { :short => 'p', :long => 'port', :argument => :required },
|
36
|
+
# ]
|
37
|
+
#
|
38
|
+
# During parsing, two errors can be raised:
|
39
|
+
#
|
40
|
+
# IllegalOptionError:: An unrecognised option was encountered, i.e. an
|
41
|
+
# option that is not present in the list of option
|
42
|
+
# definitions.
|
43
|
+
#
|
44
|
+
# OptionRequiresAnArgumentError:: An option was found that did not have a
|
45
|
+
# value, even though this value was
|
46
|
+
# required.
|
47
|
+
#
|
48
|
+
# What will be returned, is a hash with two keys, :arguments and :options.
|
49
|
+
# The :arguments value contains a list of arguments, and the :options
|
50
|
+
# value contains a hash with key-value pairs for each option. Options
|
51
|
+
# without values will have a +nil+ value instead.
|
52
|
+
#
|
53
|
+
# For example, the following commandline options (which should not be
|
54
|
+
# passed as a string, but as an array of strings):
|
55
|
+
#
|
56
|
+
# foo -xyz -a hiss -s -m please --level 50 --father=ani -n luke squeak
|
57
|
+
#
|
58
|
+
# with the following option definitions:
|
59
|
+
#
|
60
|
+
# [
|
61
|
+
# { :short => 'x', :long => 'xxx', :argument => :forbidden },
|
62
|
+
# { :short => 'y', :long => 'yyy', :argument => :forbidden },
|
63
|
+
# { :short => 'z', :long => 'zzz', :argument => :forbidden },
|
64
|
+
# { :short => 'a', :long => 'all', :argument => :forbidden },
|
65
|
+
# { :short => 's', :long => 'stuff', :argument => :optional },
|
66
|
+
# { :short => 'm', :long => 'more', :argument => :optional },
|
67
|
+
# { :short => 'l', :long => 'level', :argument => :required },
|
68
|
+
# { :short => 'f', :long => 'father', :argument => :required },
|
69
|
+
# { :short => 'n', :long => 'name', :argument => :required }
|
70
|
+
# ]
|
71
|
+
#
|
72
|
+
# will be translated into:
|
73
|
+
#
|
74
|
+
# {
|
75
|
+
# :arguments => [ 'foo', 'hiss', 'squeak' ],
|
76
|
+
# :options => {
|
77
|
+
# :xxx => true,
|
78
|
+
# :yyy => true,
|
79
|
+
# :zzz => true,
|
80
|
+
# :all => true,
|
81
|
+
# :stuff => true,
|
82
|
+
# :more => 'please',
|
83
|
+
# :level => '50',
|
84
|
+
# :father => 'ani',
|
85
|
+
# :name => 'luke'
|
86
|
+
# }
|
87
|
+
# }
|
88
|
+
def self.parse(arguments_and_options, definitions)
|
89
|
+
# Don't touch original argument
|
90
|
+
unprocessed_arguments_and_options = arguments_and_options.dup
|
91
|
+
|
92
|
+
# Initialize
|
93
|
+
arguments = []
|
94
|
+
options = {}
|
95
|
+
|
96
|
+
# Determines whether we've passed the '--' marker or not
|
97
|
+
no_more_options = false
|
98
|
+
|
99
|
+
loop do
|
100
|
+
# Get next item
|
101
|
+
e = unprocessed_arguments_and_options.shift
|
102
|
+
break if e.nil?
|
103
|
+
|
104
|
+
# Handle end-of-options marker
|
105
|
+
if e == '--'
|
106
|
+
no_more_options = true
|
107
|
+
# Handle incomplete options
|
108
|
+
elsif e =~ /^--./ and !no_more_options
|
109
|
+
# Get option key, and option value if included
|
110
|
+
if e =~ /^--([^=]+)=(.+)$/
|
111
|
+
option_key = $1
|
112
|
+
option_value = $2
|
113
|
+
else
|
114
|
+
option_key = e[2..-1]
|
115
|
+
option_value = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
# Find definition
|
119
|
+
definition = definitions.find { |d| d[:long] == option_key }
|
120
|
+
raise IllegalOptionError.new(option_key) if definition.nil?
|
121
|
+
|
122
|
+
if [ :required, :optional ].include?(definition[:argument])
|
123
|
+
# Get option value if necessary
|
124
|
+
if option_value.nil?
|
125
|
+
option_value = unprocessed_arguments_and_options.shift
|
126
|
+
if option_value.nil? || option_value =~ /^-/
|
127
|
+
if definition[:argument] == :required
|
128
|
+
raise OptionRequiresAnArgumentError.new(option_key)
|
129
|
+
else
|
130
|
+
unprocessed_arguments_and_options.unshift(option_value)
|
131
|
+
option_value = true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Store option
|
137
|
+
options[definition[:long].to_sym] = option_value
|
138
|
+
else
|
139
|
+
# Store option
|
140
|
+
options[definition[:long].to_sym] = true
|
141
|
+
end
|
142
|
+
# Handle -xyz options
|
143
|
+
elsif e =~ /^-./ and !no_more_options
|
144
|
+
# Get option keys
|
145
|
+
option_keys = e[1..-1].scan(/./)
|
146
|
+
|
147
|
+
# For each key
|
148
|
+
option_keys.each do |option_key|
|
149
|
+
# Find definition
|
150
|
+
definition = definitions.find { |d| d[:short] == option_key }
|
151
|
+
raise IllegalOptionError.new(option_key) if definition.nil?
|
152
|
+
|
153
|
+
if option_keys.length > 1 and definition[:argument] == :required
|
154
|
+
# This is a combined option and it requires an argument, so complain
|
155
|
+
raise OptionRequiresAnArgumentError.new(option_key)
|
156
|
+
elsif [ :required, :optional ].include?(definition[:argument])
|
157
|
+
# Get option value
|
158
|
+
option_value = unprocessed_arguments_and_options.shift
|
159
|
+
if option_value.nil? || option_value =~ /^-/
|
160
|
+
if definition[:argument] == :required
|
161
|
+
raise OptionRequiresAnArgumentError.new(option_key)
|
162
|
+
else
|
163
|
+
unprocessed_arguments_and_options.unshift(option_value)
|
164
|
+
option_value = true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Store option
|
169
|
+
options[definition[:long].to_sym] = option_value
|
170
|
+
else
|
171
|
+
# Store option
|
172
|
+
options[definition[:long].to_sym] = true
|
173
|
+
end
|
174
|
+
end
|
175
|
+
# Handle normal arguments
|
176
|
+
else
|
177
|
+
arguments << e
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
{ :options => options, :arguments => arguments }
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{shu-san-scripts}
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = [%q{David Love}]
|
12
|
+
s.date = %q{2011-09-23}
|
13
|
+
s.description = %q{See the README file.}
|
14
|
+
s.email = %q{david@homeunix.org.uk}
|
15
|
+
s.executables = [%q{store}]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"HISTORY",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"bin/store",
|
31
|
+
"lib/SANStore.rb",
|
32
|
+
"lib/SANStore/cli.rb",
|
33
|
+
"lib/SANStore/cli/base.rb",
|
34
|
+
"lib/SANStore/cli/commands.rb",
|
35
|
+
"lib/SANStore/cli/commands/help.rb",
|
36
|
+
"lib/SANStore/cli/commands/list_vols.rb",
|
37
|
+
"lib/SANStore/cli/commands/new_vol.rb",
|
38
|
+
"lib/SANStore/cli/logger.rb",
|
39
|
+
"lib/SANStore/cri.rb",
|
40
|
+
"lib/SANStore/cri/base.rb",
|
41
|
+
"lib/SANStore/cri/command.rb",
|
42
|
+
"lib/SANStore/cri/core_ext.rb",
|
43
|
+
"lib/SANStore/cri/core_ext/string.rb",
|
44
|
+
"lib/SANStore/cri/option_parser.rb",
|
45
|
+
"shu-san-scripts.gemspec",
|
46
|
+
"test/helper.rb",
|
47
|
+
"test/test_shu-san-scripts.rb"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://github.com/dlove24/shu-san-scripts}
|
50
|
+
s.licenses = [%q{ISC}]
|
51
|
+
s.require_paths = [%q{lib}]
|
52
|
+
s.rubygems_version = %q{1.8.6}
|
53
|
+
s.summary = %q{Scripts used to set-up and manage iSCSI targets on OpenSolaris (ZFS) systems.}
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_runtime_dependency(%q<cri>, ["~> 2.0.2"])
|
60
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
61
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
|
62
|
+
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
63
|
+
s.add_development_dependency(%q<vclog>, ["~> 1.8.1"])
|
64
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<riot>, [">= 0"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<cri>, ["~> 2.0.2"])
|
68
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
70
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
71
|
+
s.add_dependency(%q<vclog>, ["~> 1.8.1"])
|
72
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
73
|
+
s.add_dependency(%q<riot>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<cri>, ["~> 2.0.2"])
|
77
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
79
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
80
|
+
s.add_dependency(%q<vclog>, ["~> 1.8.1"])
|
81
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
82
|
+
s.add_dependency(%q<riot>, [">= 0"])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'shu-san-scripts'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|