shu-san-scripts 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,104 +0,0 @@
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\n", opt_def[:short], opt_def[:long], opt_def[:desc].wrap_and_indent(78, 20).lstrip)
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
@@ -1,8 +0,0 @@
1
- module Cri::CoreExtensions
2
- end
3
-
4
- require 'SANStore/cri/core_ext/string'
5
-
6
- class String
7
- include Cri::CoreExtensions::String
8
- end
@@ -1,41 +0,0 @@
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
@@ -1,186 +0,0 @@
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
@@ -1,35 +0,0 @@
1
- # Copyright (c) 2010-2011 David Love
2
- #
3
- # Permission to use, copy, modify, and/or distribute this software for
4
- # any purpose with or without fee is hereby granted, provided that the
5
- # above copyright notice and this permission notice appear in all copies.
6
- #
7
- # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
- # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
- # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
- # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
- # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
- # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
- # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
- #
15
-
16
- # @author David Love
17
-
18
- require "uuidtools"
19
-
20
- # Extendes the {UUIDTools::UUID} class, adding the ability to
21
- # generate sequential UUID's
22
- class UUIDTools::UUID
23
-
24
- # Increments the internal UUID representation, returning a
25
- # new UUID that is different from, but greater, than the
26
- # current sequence number
27
- def next
28
- next_uuid = self.to_i
29
- next_uuid += 1
30
-
31
- # Return the newly created UUID
32
- return UUIDTools::UUID::parse_int(next_uuid)
33
- end
34
-
35
- end
@@ -1,239 +0,0 @@
1
- # Copyright (c) 2010-2011 David Love
2
- #
3
- # Permission to use, copy, modify, and/or distribute this software for
4
- # any purpose with or without fee is hereby granted, provided that the
5
- # above copyright notice and this permission notice appear in all copies.
6
- #
7
- # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
- # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
- # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
- # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
- # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
- # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
- # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
- #
15
-
16
- # @author David Love
17
-
18
- # Defines a class capable of creating iSCSI shares using the new
19
- # COMStar framework. This classes uses the various command line tools,
20
- # not the C API.
21
- class COMStar
22
-
23
- # Create a new, ready to use, iSCSI target. Functionally this is command
24
- # is equivalent to the old "shareiscsi=on" ZFS volume property.
25
- def self.new_target(volume_path, volume_guid)
26
-
27
- # Create a new disk target for the ZFS volume
28
- SANStore::CLI::Logger.instance.log_level(:low, :create, "New SCSI block device at /dev/zvol/rdsk/#{volume_path}")
29
- disk_target = %x[sbdadm create-lu /dev/zvol/rdsk/#{volume_path}]
30
-
31
- # Get the ID of the new disk target from the output of the
32
- # target creation command
33
- id = disk_target.split(/$/)[4].split[0]
34
- SANStore::CLI::Logger.instance.log_level(:low, :info, "Using #{id} as the logical unit identifier")
35
-
36
- # Modify the just created logical unit to include the path of the
37
- # volume backing it. This makes it much easier to get rid of the
38
- # relevant volume later
39
- SANStore::CLI::Logger.instance.log_level(:low, :update, "Storing the volume GUID as the logical unit alias")
40
- modify_lu = %x[stmfadm modify-lu --lu-prop alias=#{volume_guid} #{id}]
41
-
42
- # Link the new disk target to the iSCSI framework
43
- SANStore::CLI::Logger.instance.log_level(:low, :update, "Attaching logical unit #{id} into the iSCSI framework")
44
- vol_frame = %x[stmfadm add-view #{id}]
45
-
46
- # Create the target...
47
- SANStore::CLI::Logger.instance.log_level(:low, :create, "iSCSI block target")
48
- target = %x[itadm create-target]
49
- target_name = target.split[1]
50
-
51
- # Store the volume GUID as the alias so we can find it later
52
- SANStore::CLI::Logger.instance.log_level(:low, :update, "Storing the volume GUID as the iSCSI target alias")
53
- %x[itadm modify-target --alias #{volume_guid} #{target_name}]
54
-
55
- # Return the target name to the caller
56
- return target_name
57
- end
58
-
59
- # Delete an iSCSI target. This returns the name of the underlying ZFS store in case the
60
- # caller wants to delete that as well
61
- def self.delete_target(target_name)
62
-
63
- # Before we kill the target, get the store GUID so that we can clean up properly
64
- target_map = self.TargetToGUIDMap
65
- target_guid = target_map[target_name]
66
-
67
- # Get the maps of the LU identifers to the underlying stores
68
- vol_map = self.LUToVolMap
69
-
70
- # Look for the store with the GUID of the target we want to delete
71
- SANStore::CLI::Logger.instance.log_level(:low, :info, "Looking for the name of the volume #{target_guid} backing #{target_name}")
72
- vol_name = ""
73
- vol_store = ""
74
- vol_map.each{|key, value|
75
- if value[:guid] == target_guid then
76
- # We have found the right entry, so update the name of the
77
- # volume store and the name of the volume backing it
78
- vol_name = key
79
- vol_store = value[:data_file]
80
- break
81
- end
82
- }
83
-
84
- # Abort if we can't find what we are looking for
85
- if vol_name.empty? or vol_store.empty? then
86
- SANStore::CLI::Logger.instance.log_level(:high, :error, "Could not delete the file system for the iSCSI target. The target has been removed, but the file system is still around!")
87
- return ""
88
- end
89
-
90
- # Delete the target (and force any clients off the soon to die share)
91
- SANStore::CLI::Logger.instance.log_level(:low, :warning, "Closing all sessions for #{target_name}")
92
- target = %x[itadm delete-target -f #{target_name}]
93
-
94
- # Now unlink the SCSI logical unit, so that we can free the underlying ZFS volume
95
- SANStore::CLI::Logger.instance.log_level(:low, :delete, "Removing logical units associated with the deleted target")
96
- disk_target = %x[sbdadm delete-lu #{vol_name}]
97
-
98
- # The file store is now ready for deletion. We will tell the caller what to remove, but we
99
- # don't actually do this ourselves (file systems are someone elses problem)
100
- return vol_store
101
- end
102
-
103
- # List the current iSCSI targets defined on this host
104
- def self.list_vols
105
- raw_list = %x[itadm list-target]
106
-
107
- # Create a hash for the final list of targets
108
- target_list = Array.new
109
-
110
- # Run through the raw list of targets
111
- target_array = raw_list.split(/$/)
112
- target_array.delete_at(0)
113
-
114
- target_array.each{|row|
115
- row_fragments = row.split
116
- row_hash = Hash.new
117
-
118
- row_hash[:name] = row_fragments[0]
119
- row_hash[:state] = row_fragments[1]
120
- row_hash[:sessions] = row_fragments[2]
121
-
122
- target_list << row_hash
123
- }
124
-
125
- # return the list to the caller
126
- return target_list
127
- end
128
-
129
- # Walks over the list of iSCSI targets, looking for the store GUID (stored in the target alias field).
130
- # Returns a map of the target names and associated aliases
131
- def self.TargetToGUIDMap
132
-
133
- # Get the raw map
134
- SANStore::CLI::Logger.instance.log_level(:low, :info, "Finding the GUID's associated with iSCSI targets")
135
- raw_list = %x[stmfadm list-target -v]
136
- raw_map = raw_list.split(/$/)
137
-
138
- # Create a hash from this map
139
- map_hash = Hash.new
140
- map_index = nil
141
- map_entry = nil
142
- raw_index = 0
143
-
144
- while raw_index < raw_map.length do
145
- # Is this line the start of a new target entry
146
- if raw_map[raw_index].index(/Target\:/) then
147
-
148
- # Store the old entry
149
- unless map_entry.nil? then
150
- map_hash[map_index] = map_entry
151
- end
152
-
153
- # Create a new map entry
154
- map_entry = String.new
155
- map_index = raw_map[raw_index].partition(/Target\:/)[2].strip
156
-
157
- else
158
-
159
- # Split the line to find the key and value
160
- entry = raw_map[raw_index].partition(/\s?\:\s/)
161
-
162
- case entry[0].strip
163
- when "Alias"
164
- map_entry = entry[2].strip
165
- end
166
-
167
- end
168
-
169
- raw_index += 1
170
- end
171
-
172
- # Add the last entry
173
- unless map_entry.nil? then
174
- map_hash[map_index] = map_entry
175
- end
176
-
177
- # Return the hash map
178
- return map_hash
179
-
180
- end
181
-
182
- # Walks over the list of Logical Units, working out where the ZFS volume
183
- # backing the LU is. Returns a hash, giving the correct backing store for
184
- # each LU
185
- def self.LUToVolMap
186
-
187
- # Get the raw map
188
- SANStore::CLI::Logger.instance.log_level(:low, :info, "Finding the backing stores for the logical units")
189
- raw_list = %x[stmfadm list-lu -v]
190
- raw_map = raw_list.split(/$/)
191
-
192
- # Create a hash from this map
193
- map_hash = Hash.new
194
- map_index = nil
195
- map_entry = nil
196
- raw_index = 0
197
-
198
- while raw_index < raw_map.length do
199
- # Is this line the start of a new LU entry
200
- if raw_map[raw_index].index(/LU Name\:/) then
201
-
202
- # Store the old entry
203
- unless map_entry.nil? then
204
- map_hash[map_index] = map_entry
205
- end
206
-
207
- # Create a new map entry
208
- map_entry = Hash.new
209
- map_index = raw_map[raw_index].partition(/LU Name\:/)[2].strip
210
-
211
- else
212
-
213
- # Split the line to find the key and value
214
- entry = raw_map[raw_index].partition(/\s?\:\s/)
215
-
216
- case entry[0].strip
217
- when "Alias"
218
- map_entry[:guid] = entry[2].strip
219
- when "Data File"
220
- map_entry[:data_file] = entry[2].strip
221
- end
222
-
223
- end
224
-
225
- raw_index += 1
226
- end
227
-
228
- # Add the last entry
229
- unless map_entry.nil? then
230
- map_hash[map_index] = map_entry
231
- end
232
-
233
- # Return the hash map
234
- return map_hash
235
-
236
- end
237
-
238
- end
239
-