rvc 1.0.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.
Files changed (43) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/Rakefile +39 -0
  4. data/TODO +4 -0
  5. data/VERSION +1 -0
  6. data/bin/rvc +104 -0
  7. data/lib/rvc.rb +31 -0
  8. data/lib/rvc/completion.rb +110 -0
  9. data/lib/rvc/extensions/ClusterComputeResource.rb +42 -0
  10. data/lib/rvc/extensions/ComputeResource.rb +47 -0
  11. data/lib/rvc/extensions/Datacenter.rb +36 -0
  12. data/lib/rvc/extensions/Datastore.rb +188 -0
  13. data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +38 -0
  14. data/lib/rvc/extensions/DistributedVirtualSwitch.rb +40 -0
  15. data/lib/rvc/extensions/Folder.rb +33 -0
  16. data/lib/rvc/extensions/HostSystem.rb +48 -0
  17. data/lib/rvc/extensions/ManagedEntity.rb +28 -0
  18. data/lib/rvc/extensions/Network.rb +28 -0
  19. data/lib/rvc/extensions/ResourcePool.rb +52 -0
  20. data/lib/rvc/extensions/VirtualMachine.rb +72 -0
  21. data/lib/rvc/fs.rb +123 -0
  22. data/lib/rvc/inventory.rb +125 -0
  23. data/lib/rvc/modules.rb +74 -0
  24. data/lib/rvc/modules/basic.rb +276 -0
  25. data/lib/rvc/modules/datastore.rb +63 -0
  26. data/lib/rvc/modules/host.rb +29 -0
  27. data/lib/rvc/modules/resource_pool.rb +95 -0
  28. data/lib/rvc/modules/vim.rb +128 -0
  29. data/lib/rvc/modules/vm.rb +607 -0
  30. data/lib/rvc/modules/vmrc.rb +72 -0
  31. data/lib/rvc/modules/vnc.rb +111 -0
  32. data/lib/rvc/option_parser.rb +114 -0
  33. data/lib/rvc/path.rb +37 -0
  34. data/lib/rvc/readline-ffi.rb +41 -0
  35. data/lib/rvc/shell.rb +220 -0
  36. data/lib/rvc/ttl_cache.rb +44 -0
  37. data/lib/rvc/util.rb +168 -0
  38. data/test/_test_completion.rb +27 -0
  39. data/test/_test_option_parser.rb +6 -0
  40. data/test/inventory_fixtures.rb +15 -0
  41. data/test/test_fs.rb +113 -0
  42. data/test/test_parse_path.rb +41 -0
  43. metadata +146 -0
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module RVC
22
+
23
+ class TTLCache
24
+ Entry = Struct.new(:value, :time)
25
+
26
+ def initialize ttl
27
+ @ttl = ttl
28
+ @cache = {}
29
+ end
30
+
31
+ def [] obj, sym, *args
32
+ @cache.delete_if { |k,v| v.time + @ttl < Time.now }
33
+ key = [obj,sym,*args]
34
+ if e = @cache[key]
35
+ e.value
36
+ else
37
+ value = obj.send(sym, *args)
38
+ @cache[key] = Entry.new value, Time.now
39
+ value
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,168 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module RVC
22
+ module Util
23
+ extend self
24
+
25
+ def lookup path
26
+ $shell.fs.lookup path
27
+ end
28
+
29
+ def lookup! path, type
30
+ lookup(path).tap do |obj|
31
+ err "Not found: #{path.inspect}" unless obj
32
+ err "Expected #{type} but got #{obj.class} at #{path.inspect}" unless obj.is_a? type
33
+ end
34
+ end
35
+
36
+ def menu items
37
+ items.each_with_index { |x, i| puts "#{i} #{x}" }
38
+ input = Readline.readline("? ", false)
39
+ return if !input or input.empty?
40
+ items[input.to_i]
41
+ end
42
+
43
+ def display_inventory tree, folder, indent=0, &b
44
+ tree[folder].sort_by { |k,(o,h)| o._ref }.each do |k,(o,h)|
45
+ case o
46
+ when VIM::Folder
47
+ puts "#{" "*indent}--#{k}"
48
+ display_inventory tree, o, (indent+1), &b
49
+ else
50
+ b[o,h,indent]
51
+ end
52
+ end
53
+ end
54
+
55
+ def search_path bin
56
+ ENV['PATH'].split(':').each do |x|
57
+ path = File.join(x, bin)
58
+ return path if File.exists? path
59
+ end
60
+ nil
61
+ end
62
+
63
+ UserError = Class.new(Exception)
64
+ def err msg
65
+ raise UserError.new(msg)
66
+ end
67
+
68
+ def single_connection objs
69
+ conns = objs.map { |x| x._connection rescue nil }.compact.uniq
70
+ err "No connections" if conns.size == 0
71
+ err "Objects span multiple connections" if conns.size > 1
72
+ conns[0]
73
+ end
74
+
75
+ def tasks objs, sym, args={}
76
+ progress(objs.map { |obj| obj._call :"#{sym}_Task", args })
77
+ end
78
+
79
+ def progress tasks
80
+ interested = %w(info.progress info.state info.entityName info.error info.name)
81
+ connection = single_connection tasks
82
+ connection.serviceInstance.wait_for_multiple_tasks interested, tasks do |h|
83
+ if interactive?
84
+ h.each do |task,props|
85
+ state, entityName, name = props['info.state'], props['info.entityName'], props['info.name']
86
+ name = $` if name =~ /_Task$/
87
+ if state == 'running'
88
+ text = "#{name} #{entityName}: #{state} "
89
+ progress = props['info.progress']
90
+ barlen = terminal_columns - text.size - 2
91
+ progresslen = ((progress||0)*barlen)/100
92
+ progress_bar = "[#{'=' * progresslen}#{' ' * (barlen-progresslen)}]"
93
+ $stdout.write "\e[K#{text}#{progress_bar}\n"
94
+ elsif state == 'error'
95
+ error = props['info.error']
96
+ $stdout.write "\e[K#{name} #{entityName}: #{error.fault.class.wsdl_name}: #{error.localizedMessage}\n"
97
+ else
98
+ $stdout.write "\e[K#{name} #{entityName}: #{state}\n"
99
+ end
100
+ end
101
+ $stdout.write "\e[#{h.size}A"
102
+ $stdout.flush
103
+ end
104
+ end
105
+ $stdout.write "\e[#{tasks.size}B" if interactive?
106
+ true
107
+ end
108
+
109
+ def terminal_columns
110
+ begin
111
+ require 'curses'
112
+ Curses.cols
113
+ rescue LoadError
114
+ 80
115
+ end
116
+ end
117
+
118
+ def interactive?
119
+ terminal_columns > 0
120
+ end
121
+
122
+ def tcsetpgrp pgrp=Process.getpgrp
123
+ return unless $stdin.tty?
124
+ trap('TTOU', 'SIG_IGN')
125
+ $stdin.ioctl 0x5410, [pgrp].pack('I')
126
+ trap('TTOU', 'SIG_DFL')
127
+ end
128
+
129
+ def system_fg cmd, env={}
130
+ pid = fork do
131
+ env.each { |k,v| ENV[k] = v }
132
+ Process.setpgrp
133
+ tcsetpgrp
134
+ exec cmd
135
+ end
136
+ Process.waitpid2 pid
137
+ tcsetpgrp
138
+ nil
139
+ end
140
+
141
+ def collect_children obj, path
142
+ spec = {
143
+ :objectSet => [
144
+ {
145
+ :obj => obj,
146
+ :skip => true,
147
+ :selectSet => [
148
+ RbVmomi::VIM::TraversalSpec(
149
+ :path => path,
150
+ :type => obj.class.wsdl_name
151
+ )
152
+ ]
153
+ }
154
+ ],
155
+ :propSet => [
156
+ {
157
+ :type => 'ManagedEntity',
158
+ :pathSet => %w(name),
159
+ }
160
+ ]
161
+ }
162
+
163
+ results = obj._connection.propertyCollector.RetrieveProperties(:specSet => [spec])
164
+
165
+ Hash[results.map { |r| [r['name'], r.obj] }]
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,27 @@
1
+ require 'test/unit'
2
+ require 'rvc'
3
+ require 'inventory_fixtures'
4
+
5
+ class CompletionTest < Test::Unit::TestCase
6
+ NodeBaz = FixtureNode.new
7
+ NodeBar = FixtureNode.new
8
+ NodeFoo = FixtureNode.new('bar' => NodeBar, 'baz' => NodeBaz)
9
+ Root = FixtureNode.new('foo' => NodeFoo)
10
+
11
+ def check word, expected
12
+ got = RVC::Completion::Completor[word]
13
+ assert_equal expected, got
14
+ end
15
+
16
+ def setup
17
+ @context = RVC::Context.new Root
18
+ end
19
+
20
+ def teardown
21
+ @context = nil
22
+ end
23
+
24
+ def test_simple
25
+ check
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+ require 'rvc'
3
+ require 'inventory_fixtures'
4
+
5
+ class OptionParserTest < Test::Unit::TestCase
6
+ end
@@ -0,0 +1,15 @@
1
+ class FixtureNode
2
+ include RVC::InventoryObject
3
+ attr_accessor :parent
4
+ attr_reader :children
5
+
6
+ def initialize name, children={}
7
+ @name = name
8
+ @children = children
9
+ @children.each { |k,v| v.parent = self }
10
+ end
11
+
12
+ def pretty_print pp
13
+ pp.text "Node<#{@name}>"
14
+ end
15
+ end
@@ -0,0 +1,113 @@
1
+ require 'test/unit'
2
+ require 'rvc'
3
+ require 'inventory_fixtures'
4
+
5
+ class FSTest < Test::Unit::TestCase
6
+ NodeB = FixtureNode.new 'B'
7
+ NodeC = FixtureNode.new 'C'
8
+ NodeA = FixtureNode.new('A', 'b' => NodeB, 'c' => NodeC)
9
+ Root = FixtureNode.new('ROOT', 'a' => NodeA)
10
+
11
+ def setup
12
+ @context = RVC::FS.new Root
13
+ end
14
+
15
+ def teardown
16
+ @context = nil
17
+ end
18
+
19
+ def test_new
20
+ assert_equal Root, @context.cur
21
+ assert_equal "", @context.display_path
22
+ assert_equal 0, @context.marks.size
23
+ assert_equal [''], @context.loc.path
24
+ assert_equal [['', Root]], @context.loc.stack
25
+ end
26
+
27
+ def test_lookup_simple
28
+ assert_equal nil, @context.lookup('nonexistent')
29
+ assert_equal Root, @context.lookup('.')
30
+ assert_equal Root, @context.lookup('..')
31
+ assert_equal Root, @context.lookup('...')
32
+ assert_equal NodeA, @context.lookup('a')
33
+ assert_equal NodeB, @context.lookup('a/b')
34
+ assert_equal NodeC, @context.lookup('a/b/../c')
35
+ assert_equal NodeC, @context.lookup('a/b/.../c')
36
+ end
37
+
38
+ def test_lookup_loc_nonexistent
39
+ loc = @context.lookup_loc 'nonexistent'
40
+ assert_equal nil, loc
41
+ end
42
+
43
+ def test_lookup_loc_simple
44
+ %w(a /a ./a ./a/.).each do |path|
45
+ loc = @context.lookup_loc path
46
+ assert_equal NodeA, loc.obj
47
+ assert_equal ['', 'a'], loc.path
48
+ assert_equal [['', Root], ['a', NodeA]], loc.stack
49
+ end
50
+
51
+ %w(a/b /a/b ./a/b /a/b/.).each do |path|
52
+ loc = @context.lookup_loc path
53
+ assert_equal NodeB, loc.obj
54
+ assert_equal ['', 'a', 'b'], loc.path
55
+ assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], loc.stack
56
+ end
57
+ end
58
+
59
+ def test_lookup_loc_parent
60
+ loc = @context.lookup_loc '..'
61
+ assert_equal [['', Root]], loc.stack
62
+
63
+ loc = @context.lookup_loc 'a/..'
64
+ assert_equal [['', Root]], loc.stack
65
+
66
+ loc = @context.lookup_loc 'a/b/..'
67
+ assert_equal [['', Root], ['a', NodeA]], loc.stack
68
+ end
69
+
70
+ def test_lookup_loc_realparent
71
+ loc = @context.lookup_loc '...'
72
+ assert_equal [['', Root]], loc.stack
73
+
74
+ loc = @context.lookup_loc 'a/...'
75
+ assert_equal [['', Root], ['a', NodeA], ['...', Root]], loc.stack
76
+
77
+ loc = @context.lookup_loc 'a/b/...'
78
+ assert_equal [['', Root], ['a', NodeA], ['b', NodeB], ['...', NodeA]], loc.stack
79
+ end
80
+
81
+ def test_lookup_loc_mark
82
+ b_loc = @context.lookup_loc 'a/b'
83
+ assert_not_nil b_loc
84
+
85
+ loc = @context.lookup_loc '~foo'
86
+ assert_equal nil, loc
87
+
88
+ ['foo', '~', '7', ''].each do |mark|
89
+ @context.mark mark, b_loc
90
+ loc = @context.lookup_loc "~#{mark}"
91
+ assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], loc.stack
92
+
93
+ @context.mark mark, nil
94
+ loc = @context.lookup_loc "~#{mark}"
95
+ assert_equal nil, loc
96
+ end
97
+
98
+ @context.mark '7', b_loc
99
+ loc = @context.lookup_loc "7"
100
+ assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], loc.stack
101
+
102
+ @context.mark '7', nil
103
+ loc = @context.lookup_loc "7"
104
+ assert_equal nil, loc
105
+ end
106
+
107
+ def test_cd
108
+ assert_equal false, @context.cd("nonexistent")
109
+ assert_equal [['', Root]], @context.loc.stack
110
+ assert_equal true, @context.cd("a")
111
+ assert_equal [['', Root], ['a', NodeA]], @context.loc.stack
112
+ end
113
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'rvc/path'
3
+
4
+ class ParsePathTest < Test::Unit::TestCase
5
+ def test_empty
6
+ els, absolute, trailing_slash = RVC::Path.parse('')
7
+ assert_equal [], els
8
+ assert_equal false, absolute
9
+ assert_equal false, trailing_slash
10
+ end
11
+
12
+ def test_root
13
+ els, absolute, trailing_slash = RVC::Path.parse('/')
14
+ assert_equal [], els
15
+ assert_equal true, absolute
16
+ assert_equal true, trailing_slash
17
+ end
18
+
19
+ def test_normal
20
+ els, absolute, trailing_slash = RVC::Path.parse('/foo/bar')
21
+ assert_equal %w(foo bar), els
22
+ assert_equal true, absolute
23
+ assert_equal false, trailing_slash
24
+
25
+ els, absolute, trailing_slash = RVC::Path.parse('/foo/bar/')
26
+ assert_equal %w(foo bar), els
27
+ assert_equal true, absolute
28
+ assert_equal true, trailing_slash
29
+
30
+ els, absolute, trailing_slash = RVC::Path.parse('foo/bar/')
31
+ assert_equal %w(foo bar), els
32
+ assert_equal false, absolute
33
+ assert_equal true, trailing_slash
34
+
35
+ els, absolute, trailing_slash = RVC::Path.parse('foo/bar')
36
+ assert_equal %w(foo bar), els
37
+ assert_equal false, absolute
38
+ assert_equal false, trailing_slash
39
+ end
40
+ end
41
+
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rvc
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Rich Lane
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-26 00:00:00 -07:00
14
+ default_executable: rvc
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rbvmomi
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.1.5
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: trollop
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.16.2
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: backports
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.18.2
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: ffi
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 1.0.7
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ description:
61
+ email: rlane@vmware.com
62
+ executables:
63
+ - rvc
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - LICENSE
68
+ - README.rdoc
69
+ - TODO
70
+ files:
71
+ - LICENSE
72
+ - README.rdoc
73
+ - Rakefile
74
+ - TODO
75
+ - VERSION
76
+ - bin/rvc
77
+ - lib/rvc.rb
78
+ - lib/rvc/completion.rb
79
+ - lib/rvc/extensions/ClusterComputeResource.rb
80
+ - lib/rvc/extensions/ComputeResource.rb
81
+ - lib/rvc/extensions/Datacenter.rb
82
+ - lib/rvc/extensions/Datastore.rb
83
+ - lib/rvc/extensions/DistributedVirtualPortgroup.rb
84
+ - lib/rvc/extensions/DistributedVirtualSwitch.rb
85
+ - lib/rvc/extensions/Folder.rb
86
+ - lib/rvc/extensions/HostSystem.rb
87
+ - lib/rvc/extensions/ManagedEntity.rb
88
+ - lib/rvc/extensions/Network.rb
89
+ - lib/rvc/extensions/ResourcePool.rb
90
+ - lib/rvc/extensions/VirtualMachine.rb
91
+ - lib/rvc/fs.rb
92
+ - lib/rvc/inventory.rb
93
+ - lib/rvc/modules.rb
94
+ - lib/rvc/modules/basic.rb
95
+ - lib/rvc/modules/datastore.rb
96
+ - lib/rvc/modules/host.rb
97
+ - lib/rvc/modules/resource_pool.rb
98
+ - lib/rvc/modules/vim.rb
99
+ - lib/rvc/modules/vm.rb
100
+ - lib/rvc/modules/vmrc.rb
101
+ - lib/rvc/modules/vnc.rb
102
+ - lib/rvc/option_parser.rb
103
+ - lib/rvc/path.rb
104
+ - lib/rvc/readline-ffi.rb
105
+ - lib/rvc/shell.rb
106
+ - lib/rvc/ttl_cache.rb
107
+ - lib/rvc/util.rb
108
+ - test/inventory_fixtures.rb
109
+ - test/test_fs.rb
110
+ - test/test_parse_path.rb
111
+ - test/_test_completion.rb
112
+ - test/_test_option_parser.rb
113
+ has_rdoc: true
114
+ homepage:
115
+ licenses: []
116
+
117
+ post_install_message:
118
+ rdoc_options: []
119
+
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: "0"
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: "0"
134
+ requirements: []
135
+
136
+ rubyforge_project:
137
+ rubygems_version: 1.5.3
138
+ signing_key:
139
+ specification_version: 3
140
+ summary: vSphere console UI
141
+ test_files:
142
+ - test/_test_completion.rb
143
+ - test/_test_option_parser.rb
144
+ - test/inventory_fixtures.rb
145
+ - test/test_fs.rb
146
+ - test/test_parse_path.rb