solutious-rudy 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/CHANGES.txt +40 -16
  2. data/README.rdoc +6 -6
  3. data/bin/rudy +66 -10
  4. data/bin/rudy-ec2 +3 -1
  5. data/examples/authorize.rb +15 -0
  6. data/examples/gem-test.rb +11 -5
  7. data/examples/solaris.rb +35 -0
  8. data/examples/windows.rb +101 -0
  9. data/lib/rudy.rb +7 -1
  10. data/lib/rudy/aws.rb +2 -2
  11. data/lib/rudy/aws/ec2.rb +29 -22
  12. data/lib/rudy/aws/ec2/group.rb +1 -1
  13. data/lib/rudy/aws/ec2/image.rb +1 -29
  14. data/lib/rudy/aws/ec2/instance.rb +4 -32
  15. data/lib/rudy/aws/ec2/keypair.rb +1 -6
  16. data/lib/rudy/aws/ec2/snapshot.rb +2 -20
  17. data/lib/rudy/aws/ec2/volume.rb +11 -19
  18. data/lib/rudy/aws/ec2/zone.rb +1 -6
  19. data/lib/rudy/aws/sdb.rb +1 -1
  20. data/lib/rudy/cli/aws/ec2/addresses.rb +4 -10
  21. data/lib/rudy/cli/aws/ec2/groups.rb +0 -1
  22. data/lib/rudy/cli/aws/ec2/images.rb +1 -4
  23. data/lib/rudy/cli/aws/ec2/info.rb +63 -0
  24. data/lib/rudy/cli/aws/ec2/instances.rb +3 -5
  25. data/lib/rudy/cli/aws/ec2/keypairs.rb +3 -5
  26. data/lib/rudy/cli/aws/ec2/snapshots.rb +2 -6
  27. data/lib/rudy/cli/aws/ec2/zones.rb +2 -4
  28. data/lib/rudy/cli/backups.rb +20 -9
  29. data/lib/rudy/cli/base.rb +60 -3
  30. data/lib/rudy/cli/candy.rb +1 -1
  31. data/lib/rudy/cli/disks.rb +65 -7
  32. data/lib/rudy/cli/execbase.rb +0 -2
  33. data/lib/rudy/cli/images.rb +97 -0
  34. data/lib/rudy/cli/info.rb +48 -0
  35. data/lib/rudy/cli/keypairs.rb +43 -0
  36. data/lib/rudy/cli/machines.rb +48 -38
  37. data/lib/rudy/cli/networks.rb +68 -0
  38. data/lib/rudy/cli/routines.rb +3 -10
  39. data/lib/rudy/config/objects.rb +0 -1
  40. data/lib/rudy/disks.rb +4 -0
  41. data/lib/rudy/global.rb +1 -1
  42. data/lib/rudy/huxtable.rb +9 -3
  43. data/lib/rudy/machines.rb +1 -1
  44. data/lib/rudy/metadata.rb +4 -1
  45. data/lib/rudy/metadata/backup.rb +2 -2
  46. data/lib/rudy/metadata/disk.rb +7 -4
  47. data/lib/rudy/metadata/machine.rb +66 -2
  48. data/lib/rudy/routines.rb +2 -1
  49. data/lib/rudy/routines/base.rb +4 -157
  50. data/lib/rudy/routines/handlers/base.rb +6 -3
  51. data/lib/rudy/routines/handlers/disks.rb +127 -42
  52. data/lib/rudy/routines/handlers/group.rb +45 -10
  53. data/lib/rudy/routines/handlers/host.rb +16 -10
  54. data/lib/rudy/routines/handlers/keypair.rb +26 -10
  55. data/lib/rudy/routines/handlers/rye.rb +173 -0
  56. data/lib/rudy/routines/handlers/script.rb +2 -1
  57. data/lib/rudy/routines/passthrough.rb +2 -2
  58. data/lib/rudy/routines/reboot.rb +2 -2
  59. data/lib/rudy/routines/shutdown.rb +2 -2
  60. data/lib/rudy/routines/startup.rb +4 -2
  61. data/rudy.gemspec +15 -8
  62. data/tryouts/10_require_time/10_rudy_tryouts.rb +1 -1
  63. data/tryouts/12_config/20_defaults_tryouts.rb +1 -1
  64. data/tryouts/12_config/40_machines_tryouts.rb +1 -1
  65. data/tryouts/15_huxtable/20_user_tryouts.rb +1 -1
  66. data/tryouts/25_ec2/10_keypairs_tryouts.rb +1 -0
  67. data/tryouts/30_metadata/10_include_tryouts.rb +1 -1
  68. data/tryouts/30_metadata/13_object_tryouts.rb +4 -0
  69. data/tryouts/30_metadata/50_disk_tryouts.rb +4 -2
  70. data/tryouts/30_metadata/51_disk_digest_tryouts.rb +1 -1
  71. data/tryouts/30_metadata/53_disk_list_tryouts.rb +2 -1
  72. data/tryouts/30_metadata/56_disk_volume_tryouts.rb +1 -1
  73. data/tryouts/30_metadata/60_backup_tryouts.rb +4 -2
  74. data/tryouts/30_metadata/63_backup_list_tryouts.rb +1 -1
  75. data/tryouts/30_metadata/64_backup_disk_tryouts.rb +3 -1
  76. data/tryouts/30_metadata/66_backup_snapshot_tryouts.rb +1 -1
  77. data/tryouts/30_metadata/70_machine_tryouts.rb +5 -2
  78. data/tryouts/30_metadata/73_machine_list_tryouts.rb +1 -1
  79. data/tryouts/30_metadata/76_machine_instance_tryouts.rb +15 -3
  80. data/tryouts/30_metadata/77_machines_tryouts.rb +1 -1
  81. data/tryouts/40_routines/10_keypair_handler_tryouts.rb +6 -5
  82. data/tryouts/40_routines/11_group_handler_tryouts.rb +1 -1
  83. metadata +13 -6
  84. data/lib/rudy/cli/status.rb +0 -60
data/lib/rudy.rb CHANGED
@@ -42,7 +42,7 @@ module Rudy
42
42
  unless defined?(MAJOR)
43
43
  MAJOR = 0.freeze
44
44
  MINOR = 9.freeze
45
- TINY = 1.freeze
45
+ TINY = 2.freeze
46
46
  end
47
47
  def self.to_s; [MAJOR, MINOR, TINY].join('.'); end
48
48
  def self.to_f; self.to_s.to_f; end
@@ -72,6 +72,12 @@ module Rudy
72
72
  DEFAULT_EC2_HOST = "ec2.amazonaws.com"
73
73
  DEFAULT_EC2_PORT = 443
74
74
 
75
+ DEFAULT_WINDOWS_FS = 'ntfs'
76
+ DEFAULT_LINUX_FS = 'ext3'
77
+
78
+ DEFAULT_WINDOWS_DEVICE = 'xvdf'
79
+ DEFAULT_LINUX_DEVICE = '/dev/sdh'
80
+
75
81
  MAX_INSTANCES = 5.freeze
76
82
 
77
83
  ID_MAP = {
data/lib/rudy/aws.rb CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- require 'EC2' # amazon-ec2 gem
3
+ require 'AWS' # amazon-ec2 gem
4
4
 
5
5
 
6
6
  module Rudy
@@ -27,7 +27,7 @@ module Rudy
27
27
 
28
28
  Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'aws', '{ec2,s3,sdb}', "*.rb")
29
29
 
30
- class Error < ::EC2::Error; end
30
+ class Error < ::AWS::Error; end
31
31
  end
32
32
 
33
33
  end
data/lib/rudy/aws/ec2.rb CHANGED
@@ -3,6 +3,8 @@ module Rudy; module AWS
3
3
  module EC2
4
4
  # include Rudy::Huxtable
5
5
 
6
+ @@mutex = Mutex.new
7
+
6
8
  def self.connect(access_key=nil, secret_key=nil, region=nil, logger=nil)
7
9
 
8
10
  if region
@@ -15,7 +17,7 @@ module Rudy; module AWS
15
17
  host ||= DEFAULT_EC2_HOST
16
18
  port ||= DEFAULT_EC2_PORT
17
19
 
18
- @@ec2 = ::EC2::Base.new(:port => port, :server=> host, :access_key_id => access_key, :secret_access_key => secret_key)
20
+ @@ec2 = ::AWS::EC2::Base.new(:port => port, :server=> host, :access_key_id => access_key, :secret_access_key => secret_key)
19
21
  @@logger = logger
20
22
  end
21
23
 
@@ -28,31 +30,36 @@ module Rudy; module AWS
28
30
  # Returns the return value from the request is returned untouched
29
31
  # or the default value on error or if the request returned nil.
30
32
  def self.execute_request(default=nil, timeout=nil, &request)
31
- timeout ||= 15
33
+ timeout ||= 30
32
34
  raise "No block provided" unless request
33
35
  response = nil
34
- begin
35
- Timeout::timeout(timeout) do
36
- response = request.call
37
- end
38
- # Raise the EC2 exceptions
39
- rescue ::EC2::Error, ::EC2::InvalidInstanceIDMalformed => ex
40
- raise Rudy::AWS::Error, ex.message
36
+ @@mutex.synchronize {
37
+ begin
38
+
39
+ Timeout::timeout(timeout) do
40
+ response = request.call
41
+ end
41
42
 
42
- # NOTE: The InternalError is returned for non-existent volume IDs.
43
- # It's probably a bug so we're ignoring it -- Dave.
44
- rescue ::EC2::InternalError => ex
45
- raise Rudy::AWS::Error, ex.message
43
+ # Raise the EC2 exceptions
44
+ rescue ::AWS::Error, ::AWS::InvalidInstanceIDMalformed => ex
45
+ raise Rudy::AWS::Error, ex.message
46
+
47
+ # NOTE: The InternalError is returned for non-existent volume IDs.
48
+ # It's probably a bug so we're ignoring it -- Dave.
49
+ rescue ::AWS::InternalError => ex
50
+ raise Rudy::AWS::Error, ex.message
46
51
 
47
- rescue Timeout::Error => ex
48
- STDERR.puts "Timeout (#{timeout}): #{ex.message}!"
49
- rescue SocketError => ex
50
- #STDERR.puts ex.message
51
- #STDERR.puts ex.backtrace
52
- raise SocketError, "Check your Internets!" unless @@global.offline
53
- ensure
54
- response ||= default
55
- end
52
+ rescue Timeout::Error => ex
53
+ STDERR.puts "Timeout (#{timeout}): #{ex.message}!"
54
+ rescue SocketError => ex
55
+ #STDERR.puts ex.message
56
+ #STDERR.puts ex.backtrace
57
+ raise SocketError, "Check your Internets!" unless @@global.offline
58
+ ensure
59
+ response ||= default
60
+ end
61
+ sleep 0.1 # defeat race conditions
62
+ }
56
63
  response
57
64
  end
58
65
 
@@ -171,7 +171,7 @@ module Rudy::AWS
171
171
  def exists?(name)
172
172
  begin
173
173
  g = list([name.to_s])
174
- rescue ::EC2::InvalidGroupNotFound
174
+ rescue ::AWS::InvalidGroupNotFound
175
175
  return false
176
176
  end
177
177
 
@@ -13,37 +13,9 @@ module Rudy::AWS
13
13
  field :visibility
14
14
  field :location
15
15
  field :kind
16
-
17
- def liner_note
18
- info = @location
19
- # Highlight "debian-5" in /dir/debian-5.manifest.xml
20
- #info = info.split(/\//)
21
- #info[-1].gsub!(/(.+?)((\.img)?\.manifest\.xml)/) { |m,n| ($1 || "").bright << $2 }
22
- #info = info.join('/')
23
- "%s %-6s (%s)" % [self.awsid.bright, self.arch, info]
24
- end
25
16
 
26
17
  def to_s(with_title=false)
27
- lines = []
28
- lines << liner_note
29
- #if self.available?
30
- # p = public? ? "public" : "private"
31
- # k, r = @aki || 'aki-unknown', @ari || 'ari-unknown'
32
- # lines << @@sformat % %w{arch owner aki ari visibility} if with_title
33
- # lines << @@sformat % [@arch, @owner, k, r, p]
34
- #end
35
- lines.join($/)
36
- end
37
-
38
- def inspect
39
- lines = []
40
- lines << liner_note
41
- field_names.each do |key|
42
- next unless self.respond_to?(key)
43
- val = self.send(key)
44
- lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
45
- end
46
- lines.join($/)
18
+ [@awsid.bright, @arch, @visibility, @location].join '; '
47
19
  end
48
20
 
49
21
  def available?; @state && @state == "available"; end
@@ -23,38 +23,9 @@ module Rudy::AWS
23
23
  @groups ||= []
24
24
  end
25
25
 
26
- def liner_note
27
- info = self.running? ? self.dns_public : self.state
28
- "%s %-30s" % [self.awsid.bright, info]
29
- end
30
-
31
- def to_s(with_title=false)
32
- lines = []
33
- if @groups
34
- gpstr = [@groups].flatten.compact.join(', ')
35
- gpstr &&= "(#{gpstr})"
36
- else
37
- gpstr = ''
38
- end
39
-
40
- lines << "%s %s" % [liner_note, gpstr]
41
- #if self.running?
42
- # k, g = @keyname || 'no-keypair', self.groups.join(', ')
43
- # lines << @@sformat % %w{zone size ami keyname groups} if with_title
44
- # lines << @@sformat % [@zone, @size, @ami, k, g]
45
- #end
46
- lines.join($/)
47
- end
48
-
49
- def pretty
50
- lines = []
51
- lines << liner_note
52
- field_names.each do |key|
53
- next unless self.respond_to?(key)
54
- val = self.send(key)
55
- lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
56
- end
57
- lines.join($/)
26
+ def to_s(*args)
27
+ groups = [@groups].flatten.compact.join(', ')
28
+ [self.awsid.bright, self.state, self.dns_public, groups].join '; '
58
29
  end
59
30
 
60
31
  def running?; self.state && self.state == 'running'; end
@@ -307,6 +278,7 @@ module Rudy::AWS
307
278
  # +inst_id+ is an instance ID
308
279
  # Returns an Instance object
309
280
  def get(inst_id)
281
+ return nil if inst_id.nil?
310
282
  inst_id = inst_id.awsid if inst_id.is_a?(Rudy::AWS::EC2::Instance)
311
283
  inst = list(:any, inst_id)
312
284
  inst &&= inst.first
@@ -8,13 +8,8 @@ module Rudy::AWS
8
8
  field :fingerprint
9
9
  field :private_key
10
10
 
11
- def liner_note
12
- "%-20s %s" % [self.name.bright, self.fingerprint]
13
- end
14
-
15
11
  def to_s(titles=false)
16
- str = titles ? "%-20s %s#{$/}" % ['name', 'fingerprint'] : ""
17
- str << liner_note
12
+ [@name.bright, @fingerprint].join '; '
18
13
  end
19
14
 
20
15
  def public_key
@@ -3,7 +3,6 @@
3
3
  module Rudy::AWS
4
4
  module EC2
5
5
  class Snapshot < Storable
6
- @@sformat = "%s <- %10s; %s"
7
6
 
8
7
  field :awsid
9
8
  field :progress
@@ -11,25 +10,8 @@ module Rudy::AWS
11
10
  field :volid
12
11
  field :status
13
12
 
14
- def liner_note
15
- t = Time.parse(@created)
16
- info = t.strftime("%Y-%m-%d %H:%M:%S")
17
- "%s (%s)" % [(self.awsid || '').bright, info]
18
- end
19
-
20
13
  def to_s(with_title=false)
21
- @@sformat % [liner_note, @volid, @status]
22
- end
23
-
24
- def pretty
25
- lines = []
26
- lines << liner_note
27
- field_names.each do |key|
28
- next unless self.respond_to?(key)
29
- val = self.send(key)
30
- lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
31
- end
32
- lines.join($/)
14
+ [@awsid.bright, @volid, @created, @status, @progress].join '; '
33
15
  end
34
16
 
35
17
  def completed?
@@ -98,7 +80,7 @@ module Rudy::AWS
98
80
  end
99
81
 
100
82
  def get(snap_id)
101
- list(snap_id).first || nil
83
+ list(snap_id).first rescue nil
102
84
  end
103
85
 
104
86
  def exists?(snap_id)
@@ -20,23 +20,8 @@ module Rudy::AWS
20
20
  @size &&= @size.to_i
21
21
  end
22
22
 
23
- def liner_note
24
- info = attached? ? "attached to #{@instid}" : @status
25
- "%s (%s)" % [(self.awsid || '').bright, info]
26
- end
27
-
28
- def to_s(with_title=false)
29
- line = @@sformat % [liner_note, @zone, @size, @device]
30
- line << " <- %s" % [@snapid] if @snapid
31
- line
32
- end
33
-
34
- def pretty
35
- lines = [liner_note]
36
- field_names.each do |n|
37
- lines << sprintf(" %12s: %s", n, self.send(n)) if self.send(n)
38
- end
39
- lines.join($/)
23
+ def to_s(*args)
24
+ [@awsid.bright, @zone, @size, @created, @instid, @snapid].join '; '
40
25
  end
41
26
 
42
27
  # Alias for status
@@ -155,7 +140,14 @@ module Rudy::AWS
155
140
  volumes = nil if volumes.empty?
156
141
  volumes
157
142
  end
158
-
143
+
144
+ def list_by_instance(instid, &each_vol)
145
+ instid = instid.awsid if instid.is_a? Rudy::AWS::EC2::Instance
146
+ volumes = list(:attached, &each_vol)
147
+ volumes &&= volumes.select { |v| v.instid == instid }
148
+ volumes
149
+ end
150
+
159
151
  def any?(state=nil,vol_id=[])
160
152
  vols = list(state, vol_id)
161
153
  !vols.nil?
@@ -180,7 +172,7 @@ module Rudy::AWS
180
172
  vol_id = Volumes.get_vol_id(vol_id)
181
173
  return false unless vol_id
182
174
  vol = get(vol_id)
183
- (vol && vol.status == state)
175
+ (vol && vol.status == state) ? true : false
184
176
  end
185
177
  end
186
178
 
@@ -8,13 +8,8 @@ module Rudy::AWS
8
8
  field :region
9
9
  field :state
10
10
 
11
- def liner_note
12
- "%-10s %9s %s" % [self.name, self.region, self.state]
13
- end
14
-
15
11
  def to_s(titles=false)
16
- str = titles ? "%-20s %s#{$/}" % ['name', 'region', 'state'] : ""
17
- str << liner_note
12
+ [@name.bright, @region, @state].join '; '
18
13
  end
19
14
 
20
15
  end
data/lib/rudy/aws/sdb.rb CHANGED
@@ -181,7 +181,7 @@ module Rudy
181
181
  # Returns the return value from the request is returned untouched
182
182
  # or the default value on error or if the request returned nil.
183
183
  def execute_request(default=nil, timeout=nil, &request)
184
- timeout ||= 15
184
+ timeout ||= 30
185
185
  raise "No block provided" unless request
186
186
  response = nil
187
187
  begin
@@ -7,7 +7,7 @@ module AWS; module EC2;
7
7
 
8
8
  def addresses_create
9
9
  address = Rudy::AWS::EC2::Addresses.create
10
- puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
10
+ print_stobject address
11
11
  end
12
12
 
13
13
  def addresses_destroy_valid?
@@ -24,7 +24,6 @@ module AWS; module EC2;
24
24
  puts "NOTE: this IP address will become available to other EC2 customers.".bright
25
25
  execute_check(:medium)
26
26
  execute_action { Rudy::AWS::EC2::Addresses.destroy(@argv.ipaddress) }
27
- self.addresses
28
27
  end
29
28
 
30
29
  def associate_addresses_valid?
@@ -58,7 +57,7 @@ module AWS; module EC2;
58
57
  execute_check(:low)
59
58
  execute_action { Rudy::AWS::EC2::Addresses.associate(address, instance.awsid) }
60
59
  address = Rudy::AWS::EC2::Addresses.get(address)
61
- puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
60
+ print_stobject address
62
61
  end
63
62
 
64
63
  def disassociate_addresses_valid?
@@ -76,17 +75,12 @@ module AWS; module EC2;
76
75
  execute_check(:medium)
77
76
  execute_action { Rudy::AWS::EC2::Addresses.disassociate(@argv.ipaddress) }
78
77
  address = Rudy::AWS::EC2::Addresses.get(@argv.ipaddress)
79
- puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
78
+ print_stobject address
80
79
  end
81
80
 
82
81
  def addresses
83
82
  addresses = Rudy::AWS::EC2::Addresses.list || []
84
-
85
- addresses.each do |address|
86
- puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
87
- end
88
-
89
- puts "No Addresses" if addresses.empty?
83
+ print_stobjects addresses
90
84
  end
91
85
 
92
86
 
@@ -33,7 +33,6 @@ module AWS; module EC2;
33
33
  execute_check(:medium)
34
34
  execute_action { Rudy::AWS::EC2::Groups.destroy(@argv.name) }
35
35
  @argv.clear # so groups will print all other groups
36
- groups
37
36
  end
38
37
 
39
38
  def revoke_groups_valid?; modify_group_valid?; end
@@ -21,10 +21,7 @@ module AWS; module EC2;
21
21
  end
22
22
 
23
23
  images = Rudy::AWS::EC2::Images.list(@option.owner, @argv) || []
24
- images.each do |img|
25
- puts @@global.verbose > 0 ? img.inspect : img.dump(@@global.format)
26
- end
27
- puts "No images" if images.empty?
24
+ print_stobjects images
28
25
  end
29
26
 
30
27
  def destroy_images_valid?
@@ -0,0 +1,63 @@
1
+
2
+
3
+ module Rudy; module CLI
4
+ module AWS; module EC2;
5
+
6
+ class Info < Rudy::CLI::CommandBase
7
+
8
+ def info
9
+ process_region @@global.region
10
+ oregions = Rudy::AWS::VALID_REGIONS - [@@global.region.to_sym]
11
+ if @option.all
12
+ oregions.each do |region|
13
+ Rudy::AWS::EC2.connect @@global.accesskey, @@global.secretkey, region
14
+ process_region region
15
+ end
16
+ else
17
+ puts $/, "Other regions: " << oregions.join(', ')
18
+ end
19
+ end
20
+
21
+
22
+ private
23
+ def process_region(region)
24
+ puts " Region: %s %30s".att(:reverse) % [region, '']
25
+ puts " Instances".bright
26
+ istatus = @option.all ? :any : :running
27
+ (Rudy::AWS::EC2::Instances.list(istatus) || []).collect do |inst|
28
+ #puts " %s (%s): %s; %s; %s" % [inst.awsid, inst.state, inst.dns_public || '[no dns]', inst.size, inst.created]
29
+ puts " #{inst.to_s.noatt}"
30
+ end
31
+
32
+ puts " Volumes".bright
33
+ (Rudy::AWS::EC2::Volumes.list || []).collect do |vol|
34
+ puts " %s (%s): %sGB; %s" % [vol.awsid, vol.instid || vol.status, vol.size, vol.created]
35
+ end
36
+
37
+ puts " Snapshots".bright
38
+ (Rudy::AWS::EC2::Snapshots.list || []).collect do |snap|
39
+ puts " %s: %s; %s" % [snap.awsid, snap.volid, snap.created]
40
+ end
41
+
42
+ puts " Addresses".bright
43
+ (Rudy::AWS::EC2::Addresses.list || []).collect do |o|
44
+ puts " %s (%s)" % [o.ipaddress, o.instid || 'available']
45
+ end
46
+
47
+ puts " Groups".bright
48
+ puts (Rudy::AWS::EC2::Groups.list || []).collect { |o| " #{o.name}" }
49
+
50
+ puts " Keypairs".bright
51
+ puts (Rudy::AWS::EC2::Keypairs.list || []).collect { |o| " #{o.name}" }
52
+
53
+ puts " Images".bright
54
+ (Rudy::AWS::EC2::Images.list('self') || []).collect do |o|
55
+ puts " %s: %s; %s; %s" % [o.awsid, o.location, o.arch, o.visibility]
56
+ end
57
+ puts
58
+ end
59
+
60
+ end
61
+
62
+ end; end
63
+ end; end