rvc 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +10 -1
- data/VERSION +1 -1
- data/bin/rvc +8 -6
- data/lib/rvc/completion.rb +8 -8
- data/lib/rvc/fs.rb +46 -82
- data/lib/rvc/inventory.rb +21 -14
- data/lib/rvc/known_hosts.rb +46 -0
- data/lib/rvc/modules/basic.rb +40 -36
- data/lib/rvc/modules/datastore.rb +26 -0
- data/lib/rvc/modules/mark.rb +56 -0
- data/lib/rvc/modules/vim.rb +30 -13
- data/lib/rvc/option_parser.rb +5 -2
- data/lib/rvc/shell.rb +8 -6
- data/lib/rvc/util.rb +11 -1
- data/test/test_fs.rb +60 -59
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -81,6 +81,13 @@ When the working directory is a descendant of a Datacenter object, the mark "~"
|
|
81
81
|
refers to the Datacenter. For example "~/datastore" is a convenient way to get
|
82
82
|
the datastore folder of the current datacenter.
|
83
83
|
|
84
|
+
=== Aggregate marks
|
85
|
+
|
86
|
+
More than one object can be given to the "mark" command. The resulting mark can
|
87
|
+
be used with any command that accepts multiple objects. The "mark.edit" command
|
88
|
+
opens up an editor showing the objects referenced by the given mark and allows
|
89
|
+
you to remove some or add more.
|
90
|
+
|
84
91
|
=== Ruby mode
|
85
92
|
|
86
93
|
Beginning an input line with "/" causes RVC to treat it as Ruby code and eval
|
@@ -89,7 +96,9 @@ it. This gives you direct access to the underlying RbVmomi library. If the line
|
|
89
96
|
|
90
97
|
Marks can be easily used in Ruby mode since there are magic variables with the
|
91
98
|
same names. Since some marks, like numeric ones, aren't valid variable names,
|
92
|
-
they also exist with a "_" prefix.
|
99
|
+
they also exist with a "_" prefix. By default only the first object in an
|
100
|
+
aggregate mark will be returned; to get an array of them all use the '!'
|
101
|
+
suffix.
|
93
102
|
|
94
103
|
The methods "this", "conn", and "dc" are provided in Ruby mode, returning the
|
95
104
|
current object, connection, and datacenter respectively. The connection object
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
data/bin/rvc
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
20
|
# THE SOFTWARE.
|
21
21
|
require 'readline'
|
22
|
+
require "highline/import"
|
22
23
|
require 'pp'
|
23
24
|
require 'trollop'
|
24
25
|
require 'rbvmomi'
|
@@ -49,6 +50,7 @@ where [options] are:
|
|
49
50
|
EOS
|
50
51
|
|
51
52
|
opt :insecure, "don't verify ssl certificate", :short => 'k', :default => (ENV['RBVMOMI_INSECURE'] == '1')
|
53
|
+
opt :really_insecure, "don't use ~/.rvc/known_hosts", :short => 'K', :default => (ENV['RBVMOMI_REALLY_INSECURE'] == '1')
|
52
54
|
opt :path, "Initial directory", :short => :none, :default => ENV['RVC_PATH'], :type => :string
|
53
55
|
opt :create_directory, "Create the initial directory if it doesn't exist", :short => :none
|
54
56
|
opt :cmd, "command to evaluate", :short => 'c', :multi => true, :type => :string
|
@@ -63,7 +65,7 @@ $shell = RVC::Shell.new
|
|
63
65
|
ARGV.each do |uri|
|
64
66
|
begin
|
65
67
|
puts "Connecting to #{uri}..." if ARGV.size > 1
|
66
|
-
CMD.vim.connect uri, :insecure => $opts[:insecure]
|
68
|
+
CMD.vim.connect uri, :insecure => $opts[:insecure], :really_insecure => $opts[:really_insecure]
|
67
69
|
rescue UserError
|
68
70
|
puts "Failed to connect to #{uri}: #{$!.message}"
|
69
71
|
exit 1
|
@@ -81,15 +83,15 @@ if $opts[:path]
|
|
81
83
|
rescue UserError
|
82
84
|
raise unless $opts[:create_directory]
|
83
85
|
parent_path = File.dirname($opts[:path])
|
84
|
-
|
85
|
-
CMD.basic.cd $opts[:path]
|
86
|
+
lookup_single(parent_path).CreateFolder(:name => File.basename($opts[:path]))
|
87
|
+
CMD.basic.cd lookup_single($opts[:path])
|
86
88
|
end
|
87
89
|
elsif $shell.connections.size == 1
|
88
90
|
conn_name, conn = $shell.connections.first
|
89
91
|
if conn.serviceContent.about.apiType == 'VirtualCenter'
|
90
|
-
CMD.basic.cd conn_name
|
92
|
+
CMD.basic.cd lookup_single(conn_name)
|
91
93
|
else
|
92
|
-
CMD.basic.cd "#{conn_name}/ha-datacenter/vm"
|
94
|
+
CMD.basic.cd lookup_single("#{conn_name}/ha-datacenter/vm")
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -97,7 +99,7 @@ unless CMD.vmrc.find_vmrc
|
|
97
99
|
$stderr.puts "VMRC is not installed. You will be unable to view virtual machine consoles. Use the vmrc.install command to install it."
|
98
100
|
end
|
99
101
|
|
100
|
-
CMD.basic.ls '.'
|
102
|
+
CMD.basic.ls lookup_single('.')
|
101
103
|
|
102
104
|
while true
|
103
105
|
begin
|
data/lib/rvc/completion.rb
CHANGED
@@ -87,18 +87,18 @@ module Completion
|
|
87
87
|
ret.sort.select { |e| e.match(prefix_regex) }
|
88
88
|
end
|
89
89
|
|
90
|
+
# TODO convert to globbing
|
90
91
|
def self.child_candidates word
|
91
|
-
|
92
|
-
last = trailing_slash ? '' : (
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
els.unshift '' if absolute
|
92
|
+
arcs, absolute, trailing_slash = Path.parse word
|
93
|
+
last = trailing_slash ? '' : (arcs.pop || '')
|
94
|
+
arcs.map! { |x| x.gsub '\\', '' }
|
95
|
+
base = absolute ? $shell.fs.root : $shell.fs.cur
|
96
|
+
cur = $shell.fs.traverse(base, arcs).first or return []
|
97
|
+
arcs.unshift '' if absolute
|
98
98
|
children = Cache[cur, :children] rescue []
|
99
99
|
children.
|
100
100
|
select { |k,v| k.gsub(' ', '\\ ') =~ /^#{Regexp.escape(last)}/ }.
|
101
|
-
map { |k,v| (
|
101
|
+
map { |k,v| (arcs+[k])*'/' }.
|
102
102
|
map { |x| x.gsub ' ', '\\ ' }
|
103
103
|
end
|
104
104
|
|
data/lib/rvc/fs.rb
CHANGED
@@ -20,125 +20,89 @@
|
|
20
20
|
|
21
21
|
module RVC
|
22
22
|
|
23
|
-
class Location
|
24
|
-
attr_reader :stack
|
25
|
-
|
26
|
-
def initialize root
|
27
|
-
@stack = [['', root]]
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize_copy src
|
31
|
-
super
|
32
|
-
@stack = @stack.dup
|
33
|
-
end
|
34
|
-
|
35
|
-
def push name, obj
|
36
|
-
@stack << [name, obj]
|
37
|
-
end
|
38
|
-
|
39
|
-
def pop
|
40
|
-
@stack.pop
|
41
|
-
end
|
42
|
-
|
43
|
-
def obj
|
44
|
-
@stack.empty? ? nil : @stack[-1][1]
|
45
|
-
end
|
46
|
-
|
47
|
-
def path
|
48
|
-
@stack.map { |name,obj| name }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
23
|
class FS
|
53
|
-
attr_reader :root, :
|
24
|
+
attr_reader :root, :cur, :marks
|
54
25
|
|
55
26
|
MARK_PATTERN = /^~(?:([\d\w]*|~|@))$/
|
56
27
|
REGEX_PATTERN = /^%/
|
57
28
|
GLOB_PATTERN = /\*/
|
58
29
|
|
59
30
|
def initialize root
|
31
|
+
fail unless root.is_a? RVC::InventoryObject
|
60
32
|
@root = root
|
61
|
-
@
|
33
|
+
@cur = root
|
62
34
|
@marks = {}
|
63
35
|
end
|
64
36
|
|
65
|
-
def cur
|
66
|
-
@loc.obj
|
67
|
-
end
|
68
|
-
|
69
37
|
def display_path
|
70
|
-
@
|
38
|
+
@cur.rvc_path.map { |arc,obj| arc } * '/'
|
71
39
|
end
|
72
40
|
|
73
|
-
def cd
|
74
|
-
|
75
|
-
|
41
|
+
def cd dst
|
42
|
+
fail unless dst.is_a? RVC::InventoryObject
|
43
|
+
mark '~', [@cur]
|
44
|
+
@cur = dst
|
76
45
|
end
|
77
46
|
|
78
47
|
def lookup path
|
79
|
-
|
48
|
+
arcs, absolute, trailing_slash = Path.parse path
|
49
|
+
base = absolute ? @root : @cur
|
50
|
+
traverse(base, arcs)
|
80
51
|
end
|
81
52
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
53
|
+
# Starting from base, traverse each path element in arcs. Since the path
|
54
|
+
# may contain wildcards, this function returns a list of matches.
|
55
|
+
def traverse base, arcs
|
56
|
+
objs = [base]
|
57
|
+
arcs.each_with_index do |arc,i|
|
58
|
+
objs.map! { |obj| traverse_one obj, arc, i==0 }
|
59
|
+
objs.flatten!
|
60
|
+
end
|
61
|
+
objs
|
86
62
|
end
|
87
63
|
|
88
|
-
def traverse_one
|
89
|
-
case
|
64
|
+
def traverse_one cur, arc, first
|
65
|
+
case arc
|
90
66
|
when '.'
|
91
|
-
[
|
67
|
+
[cur]
|
92
68
|
when '..'
|
93
|
-
|
94
|
-
[loc]
|
69
|
+
[cur.rvc_parent ? cur.rvc_parent : cur]
|
95
70
|
when '...'
|
96
|
-
|
97
|
-
[
|
71
|
+
# XXX shouldnt be nil
|
72
|
+
[(cur.respond_to?(:parent) && cur.parent) ? cur.parent : (cur.rvc_parent || cur)]
|
98
73
|
when MARK_PATTERN
|
99
|
-
|
100
|
-
|
101
|
-
|
74
|
+
if first and objs = @marks[$1]
|
75
|
+
objs
|
76
|
+
else
|
77
|
+
[]
|
78
|
+
end
|
102
79
|
when REGEX_PATTERN
|
103
80
|
regex = Regexp.new($')
|
104
|
-
|
105
|
-
select { |k,v| k =~ regex }.
|
106
|
-
map { |k,v| loc.dup.tap { |x| x.push(k, v) } }
|
81
|
+
cur.children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
|
107
82
|
when GLOB_PATTERN
|
108
|
-
regex = glob_to_regex
|
109
|
-
|
110
|
-
select { |k,v| k =~ regex }.
|
111
|
-
map { |k,v| loc.dup.tap { |x| x.push(k, v) } }
|
83
|
+
regex = glob_to_regex arc
|
84
|
+
cur.children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
|
112
85
|
else
|
113
86
|
# XXX check for ambiguous child
|
114
|
-
if first and
|
115
|
-
|
87
|
+
if first and arc =~ /^\d+$/ and objs = @marks[arc]
|
88
|
+
objs
|
116
89
|
else
|
117
|
-
|
118
|
-
|
90
|
+
if child = cur.traverse_one(arc)
|
91
|
+
child.rvc_link cur, arc
|
92
|
+
[child]
|
93
|
+
else
|
94
|
+
[]
|
95
|
+
end
|
119
96
|
end
|
120
|
-
[loc]
|
121
97
|
end
|
122
98
|
end
|
123
99
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
locs = [base_loc.dup]
|
128
|
-
els.each_with_index do |el,i|
|
129
|
-
locs.map! { |loc| traverse_one loc, el, i==0 }
|
130
|
-
locs.flatten!
|
131
|
-
end
|
132
|
-
locs
|
100
|
+
def mark key, objs
|
101
|
+
fail "not an array" unless objs.is_a? Array
|
102
|
+
@marks[key] = objs
|
133
103
|
end
|
134
104
|
|
135
|
-
|
136
|
-
if loc == nil
|
137
|
-
@marks.delete key
|
138
|
-
else
|
139
|
-
@marks[key] = loc
|
140
|
-
end
|
141
|
-
end
|
105
|
+
private
|
142
106
|
|
143
107
|
def glob_to_regex str
|
144
108
|
Regexp.new "^#{Regexp.escape(str.gsub('*', "\0")).gsub("\0", ".*")}$"
|
data/lib/rvc/inventory.rb
CHANGED
@@ -35,6 +35,8 @@ module InventoryObject
|
|
35
35
|
m.extend ClassMethods
|
36
36
|
end
|
37
37
|
|
38
|
+
attr_reader :rvc_parent, :rvc_arc
|
39
|
+
|
38
40
|
def display_info
|
39
41
|
puts "class: #{self.class.name}"
|
40
42
|
end
|
@@ -51,8 +53,25 @@ module InventoryObject
|
|
51
53
|
{}
|
52
54
|
end
|
53
55
|
|
54
|
-
def
|
55
|
-
|
56
|
+
def rvc_path
|
57
|
+
[].tap do |a|
|
58
|
+
cur = self
|
59
|
+
while cur != nil
|
60
|
+
a << [cur.rvc_arc, cur]
|
61
|
+
cur = cur.rvc_parent
|
62
|
+
end
|
63
|
+
a.reverse!
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def rvc_path_str
|
68
|
+
rvc_path.map { |k,v| k } * '/'
|
69
|
+
end
|
70
|
+
|
71
|
+
def rvc_link parent, arc
|
72
|
+
return if @rvc_parent
|
73
|
+
@rvc_parent = parent
|
74
|
+
@rvc_arc = arc
|
56
75
|
end
|
57
76
|
end
|
58
77
|
|
@@ -72,10 +91,6 @@ class FakeFolder
|
|
72
91
|
true
|
73
92
|
end
|
74
93
|
|
75
|
-
def parent
|
76
|
-
@target
|
77
|
-
end
|
78
|
-
|
79
94
|
def eql? x
|
80
95
|
@target == x.instance_variable_get(:@target) &&
|
81
96
|
@method == x.instance_variable_get(:@method)
|
@@ -93,10 +108,6 @@ class RootNode
|
|
93
108
|
$shell.connections
|
94
109
|
end
|
95
110
|
|
96
|
-
def parent
|
97
|
-
nil
|
98
|
-
end
|
99
|
-
|
100
111
|
def self.folder?
|
101
112
|
true
|
102
113
|
end
|
@@ -115,10 +126,6 @@ class RbVmomi::VIM
|
|
115
126
|
rootFolder.children
|
116
127
|
end
|
117
128
|
|
118
|
-
def parent
|
119
|
-
$shell.fs.root
|
120
|
-
end
|
121
|
-
|
122
129
|
def self.folder?
|
123
130
|
true
|
124
131
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'digest/sha2'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module RVC
|
5
|
+
|
6
|
+
class KnownHosts
|
7
|
+
def filename
|
8
|
+
File.join(ENV['HOME'], ".rvc", "known_hosts");
|
9
|
+
end
|
10
|
+
|
11
|
+
def hash_host protocol, hostname
|
12
|
+
Digest::SHA2.hexdigest([protocol, hostname] * "\0")
|
13
|
+
end
|
14
|
+
|
15
|
+
def hash_public_key public_key
|
16
|
+
Digest::SHA2.hexdigest(public_key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify protocol, hostname, public_key
|
20
|
+
expected_hashed_host = hash_host protocol, hostname
|
21
|
+
expected_hashed_public_key = hash_public_key public_key
|
22
|
+
if File.exists? filename
|
23
|
+
fail "bad permissions on known_hosts, expected 0600" unless File.stat(filename).mode & 0666 == 0600
|
24
|
+
File.readlines(filename).each_with_index do |l,i|
|
25
|
+
hashed_host, hashed_public_key = l.split
|
26
|
+
next unless hashed_host == expected_hashed_host
|
27
|
+
if hashed_public_key == expected_hashed_public_key
|
28
|
+
return :ok
|
29
|
+
else
|
30
|
+
return :mismatch, i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
return :not_found, expected_hashed_public_key
|
35
|
+
end
|
36
|
+
|
37
|
+
def add protocol, hostname, public_key
|
38
|
+
FileUtils.mkdir_p File.dirname(filename)
|
39
|
+
File.open(filename, 'a') do |io|
|
40
|
+
io.chmod 0600
|
41
|
+
io.write "#{hash_host protocol, hostname} #{hash_public_key public_key}\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/rvc/modules/basic.rb
CHANGED
@@ -113,48 +113,41 @@ end
|
|
113
113
|
|
114
114
|
opts :cd do
|
115
115
|
summary "Change directory"
|
116
|
-
arg :
|
116
|
+
arg :obj, "Directory to change to", :lookup => Object
|
117
117
|
end
|
118
118
|
|
119
119
|
rvc_alias :cd
|
120
120
|
|
121
|
-
def cd
|
122
|
-
|
123
|
-
|
124
|
-
$shell.fs.
|
125
|
-
$shell.fs.mark '', find_ancestor_loc(RbVmomi::VIM::Datacenter)
|
126
|
-
$shell.fs.mark '@', find_ancestor_loc(RbVmomi::VIM)
|
121
|
+
def cd obj
|
122
|
+
$shell.fs.cd(obj)
|
123
|
+
$shell.fs.mark '', [find_ancestor(RbVmomi::VIM::Datacenter)].compact
|
124
|
+
$shell.fs.mark '@', [find_ancestor(RbVmomi::VIM)].compact
|
127
125
|
$shell.fs.marks.delete_if { |k,v| k =~ /^\d+$/ }
|
128
126
|
end
|
129
127
|
|
130
|
-
def
|
131
|
-
|
132
|
-
dc_loc.pop while dc_loc.obj and not dc_loc.obj.is_a? klass
|
133
|
-
dc_loc.obj ? dc_loc : nil
|
128
|
+
def find_ancestor klass
|
129
|
+
$shell.fs.cur.rvc_path.map { |k,v| v }.reverse.find { |x| x.is_a? klass }
|
134
130
|
end
|
135
131
|
|
136
132
|
|
137
133
|
opts :ls do
|
138
134
|
summary "List objects in a directory"
|
139
|
-
arg :
|
135
|
+
arg :obj, "Directory to list", :required => false, :default => '.', :lookup => Object
|
140
136
|
end
|
141
137
|
|
142
138
|
rvc_alias :ls
|
143
139
|
rvc_alias :ls, :l
|
144
140
|
|
145
|
-
def ls
|
146
|
-
# XXX check for multiple matches
|
147
|
-
loc = $shell.fs.lookup_loc(path).first or err "Not found: #{path.inspect}"
|
148
|
-
obj = loc.obj
|
141
|
+
def ls obj
|
149
142
|
children = obj.children
|
150
143
|
name_map = children.invert
|
151
144
|
children, fake_children = children.partition { |k,v| v.is_a? VIM::ManagedEntity }
|
152
145
|
i = 0
|
153
146
|
|
154
|
-
fake_children.each do |name,
|
155
|
-
puts "#{i} #{name}#{
|
156
|
-
|
157
|
-
$shell.fs.mark i.to_s,
|
147
|
+
fake_children.each do |name,child|
|
148
|
+
puts "#{i} #{name}#{child.ls_text(nil)}"
|
149
|
+
child.rvc_link obj, name
|
150
|
+
$shell.fs.mark i.to_s, [child]
|
158
151
|
i += 1
|
159
152
|
end
|
160
153
|
|
@@ -163,9 +156,9 @@ def ls path
|
|
163
156
|
filterSpec = VIM.PropertyFilterSpec(:objectSet => [], :propSet => [])
|
164
157
|
filteredTypes = Set.new
|
165
158
|
|
166
|
-
children.each do |name,
|
167
|
-
filterSpec.objectSet << { :obj =>
|
168
|
-
filteredTypes <<
|
159
|
+
children.each do |name,child|
|
160
|
+
filterSpec.objectSet << { :obj => child }
|
161
|
+
filteredTypes << child.class
|
169
162
|
end
|
170
163
|
|
171
164
|
filteredTypes.each do |x|
|
@@ -183,8 +176,8 @@ def ls path
|
|
183
176
|
text = r.obj.ls_text(r) rescue " (error)"
|
184
177
|
realname = r['name'] if name != r['name']
|
185
178
|
puts "#{i} #{name}#{realname && " [#{realname}]"}#{text}"
|
186
|
-
|
187
|
-
$shell.fs.mark i.to_s,
|
179
|
+
r.obj.rvc_link obj, name
|
180
|
+
$shell.fs.mark i.to_s, [r.obj]
|
188
181
|
i += 1
|
189
182
|
end
|
190
183
|
end
|
@@ -199,6 +192,7 @@ rvc_alias :info
|
|
199
192
|
rvc_alias :info, :i
|
200
193
|
|
201
194
|
def info obj
|
195
|
+
puts "path: #{obj.rvc_path_str}"
|
202
196
|
if obj.respond_to? :display_info
|
203
197
|
obj.display_info
|
204
198
|
else
|
@@ -219,20 +213,30 @@ def destroy objs
|
|
219
213
|
end
|
220
214
|
|
221
215
|
|
222
|
-
opts :
|
223
|
-
summary "
|
224
|
-
arg :
|
225
|
-
|
216
|
+
opts :reload_entity do
|
217
|
+
summary "Synchronize management server state"
|
218
|
+
arg :obj, nil, :lookup => VIM::ManagedEntity, :multi => true
|
219
|
+
end
|
220
|
+
|
221
|
+
rvc_alias :reload_entity
|
222
|
+
|
223
|
+
def reload_entity objs
|
224
|
+
objs.each(&:Reload)
|
226
225
|
end
|
227
226
|
|
228
|
-
rvc_alias :mark
|
229
|
-
rvc_alias :mark, :m
|
230
227
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
228
|
+
opts :show do
|
229
|
+
summary "Basic information about the given objects"
|
230
|
+
arg :obj, nil, :multi => true, :required => false, :lookup => Object
|
231
|
+
end
|
232
|
+
|
233
|
+
rvc_alias :show
|
234
|
+
rvc_alias :show, :w
|
235
|
+
|
236
|
+
def show objs
|
237
|
+
objs.each do |obj|
|
238
|
+
puts "#{obj.rvc_path_str}: #{obj.class}"
|
239
|
+
end
|
236
240
|
end
|
237
241
|
|
238
242
|
|
@@ -30,6 +30,7 @@ def download datastore_path, local_path
|
|
30
30
|
file.datastore.download file.path, local_path
|
31
31
|
end
|
32
32
|
|
33
|
+
|
33
34
|
opts :upload do
|
34
35
|
summary "Upload a file to a datastore"
|
35
36
|
arg 'local-path', "Filename on the local machine"
|
@@ -45,6 +46,7 @@ def upload local_path, datastore_path
|
|
45
46
|
dir.datastore.upload real_datastore_path, local_path
|
46
47
|
end
|
47
48
|
|
49
|
+
|
48
50
|
opts :mkdir do
|
49
51
|
summary "Create a directory on a datastore"
|
50
52
|
arg 'path', "Directory to create on the datastore"
|
@@ -61,3 +63,27 @@ def mkdir datastore_path
|
|
61
63
|
:datacenter => dc,
|
62
64
|
:createParentDirectories => false
|
63
65
|
end
|
66
|
+
|
67
|
+
|
68
|
+
opts :edit do
|
69
|
+
summary "Edit a file"
|
70
|
+
arg "file", nil, :lookup => VIM::Datastore::FakeDatastoreFile
|
71
|
+
end
|
72
|
+
|
73
|
+
rvc_alias :edit, :vi
|
74
|
+
|
75
|
+
def edit file
|
76
|
+
editor = ENV['VISUAL'] || ENV['EDITOR'] || 'vi'
|
77
|
+
filename = File.join(Dir.tmpdir, "rvc.#{Time.now.to_i}.#{rand(65536)}")
|
78
|
+
file.datastore.download(file.path, filename) rescue err("download failed")
|
79
|
+
begin
|
80
|
+
pre_stat = File.stat filename
|
81
|
+
system("#{editor} #{filename}")
|
82
|
+
post_stat = File.stat filename
|
83
|
+
if pre_stat != post_stat
|
84
|
+
file.datastore.upload(file.path, filename) rescue err("upload failed")
|
85
|
+
end
|
86
|
+
ensure
|
87
|
+
File.unlink filename
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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
|
+
opts :mark do
|
22
|
+
summary "Save an object for later use"
|
23
|
+
arg :key, "Name for this mark"
|
24
|
+
arg :obj, "Any objects", :required => false, :default => ['.'], :multi => true, :lookup => Object
|
25
|
+
end
|
26
|
+
|
27
|
+
rvc_alias :mark
|
28
|
+
rvc_alias :mark, :m
|
29
|
+
|
30
|
+
def mark key, objs
|
31
|
+
err "invalid mark name" unless key =~ /^\w+$/
|
32
|
+
$shell.fs.mark key, objs
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
opts :edit do
|
37
|
+
summary "Edit objects referenced by a mark"
|
38
|
+
arg :key, "Name of mark"
|
39
|
+
end
|
40
|
+
|
41
|
+
rvc_alias :edit, :me
|
42
|
+
|
43
|
+
def edit key
|
44
|
+
editor = ENV['VISUAL'] || ENV['EDITOR'] || 'vi'
|
45
|
+
objs = $shell.fs.marks[key] or err "no such mark #{key.inspect}"
|
46
|
+
filename = File.join(Dir.tmpdir, "rvc.#{Time.now.to_i}.#{rand(65536)}")
|
47
|
+
File.open(filename, 'w') { |io| objs.each { |obj| io.puts(obj.rvc_path_str) } }
|
48
|
+
begin
|
49
|
+
system("#{editor} #{filename}")
|
50
|
+
new_paths = File.readlines(filename).map(&:chomp) rescue return
|
51
|
+
new_objs = new_paths.map { |path| lookup(path) }.inject([], &:+)
|
52
|
+
mark key, new_objs
|
53
|
+
ensure
|
54
|
+
File.unlink filename
|
55
|
+
end
|
56
|
+
end
|
data/lib/rvc/modules/vim.rb
CHANGED
@@ -18,6 +18,8 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'rvc/known_hosts'
|
22
|
+
|
21
23
|
URI_REGEX = %r{
|
22
24
|
^
|
23
25
|
(?:
|
@@ -63,13 +65,34 @@ def connect uri, opts
|
|
63
65
|
:insecure => insecure
|
64
66
|
break
|
65
67
|
rescue OpenSSL::SSL::SSLError
|
66
|
-
err "Connection failed" unless
|
68
|
+
err "Connection failed" unless prompt_cert_insecure
|
67
69
|
insecure = true
|
68
70
|
rescue Errno::EHOSTUNREACH, SocketError
|
69
71
|
err $!.message
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
75
|
+
if opts[:really_insecure]
|
76
|
+
result = :ok
|
77
|
+
else
|
78
|
+
peer_public_key = vim.http.peer_cert.public_key
|
79
|
+
known_hosts = RVC::KnownHosts.new
|
80
|
+
result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
if result == :not_found
|
84
|
+
puts "The authenticity of host '#{host}' can't be established."
|
85
|
+
puts "Public key fingerprint is #{arg}."
|
86
|
+
err "Connection failed" unless prompt_cert_unknown
|
87
|
+
puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
|
88
|
+
known_hosts.add 'vim', host, peer_public_key.to_s
|
89
|
+
elsif result == :mismatch
|
90
|
+
err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
|
91
|
+
elsif result == :ok
|
92
|
+
else
|
93
|
+
err "Unexpected result from known_hosts check"
|
94
|
+
end
|
95
|
+
|
73
96
|
unless opts[:rev]
|
74
97
|
# negotiate API version
|
75
98
|
rev = vim.serviceContent.about.apiVersion
|
@@ -114,19 +137,13 @@ def connect uri, opts
|
|
114
137
|
end
|
115
138
|
|
116
139
|
def prompt_password
|
117
|
-
|
118
|
-
$stdout.write "password: "
|
119
|
-
$stdout.flush
|
120
|
-
begin
|
121
|
-
($stdin.gets||exit(1)).chomp
|
122
|
-
ensure
|
123
|
-
system "stty echo"
|
124
|
-
puts
|
125
|
-
end
|
140
|
+
ask("password: ") { |q| q.echo = false }
|
126
141
|
end
|
127
142
|
|
128
|
-
def
|
129
|
-
|
130
|
-
answer == 'yes' or answer == 'y'
|
143
|
+
def prompt_cert_insecure
|
144
|
+
agree("SSL certificate verification failed. Connect anyway (y/n)? ", true)
|
131
145
|
end
|
132
146
|
|
147
|
+
def prompt_cert_unknown
|
148
|
+
agree("Are you sure you want to continue connecting (y/n)? ", true)
|
149
|
+
end
|
data/lib/rvc/option_parser.rb
CHANGED
@@ -111,9 +111,12 @@ class OptionParser < Trollop::Parser
|
|
111
111
|
|
112
112
|
def postprocess_arg x, spec
|
113
113
|
if spec[:lookup]
|
114
|
-
lookup!
|
114
|
+
lookup!(x, spec[:lookup]).
|
115
|
+
tap { |a| err "no matches for #{x.inspect}" if a.empty? }
|
115
116
|
elsif spec[:lookup_parent]
|
116
|
-
lookup!(File.dirname(x), spec[:lookup_parent]).
|
117
|
+
lookup!(File.dirname(x), spec[:lookup_parent]).
|
118
|
+
map { |y| [y, File.basename(x)] }.
|
119
|
+
tap { |a| err "no matches for #{File.dirname(x).inspect}" if a.empty? }
|
117
120
|
else
|
118
121
|
[x]
|
119
122
|
end
|
data/lib/rvc/shell.rb
CHANGED
@@ -81,7 +81,7 @@ class Shell
|
|
81
81
|
err "invalid command" unless cmd.is_a? String
|
82
82
|
case cmd
|
83
83
|
when RVC::FS::MARK_PATTERN
|
84
|
-
CMD.basic.cd cmd
|
84
|
+
CMD.basic.cd lookup_single(cmd)
|
85
85
|
else
|
86
86
|
if cmd.include? '.'
|
87
87
|
module_name, cmd, = cmd.split '.'
|
@@ -123,7 +123,7 @@ class Shell
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def prompt
|
126
|
-
"#{@fs.display_path}#{@persist_ruby ? '~' : '>'} "
|
126
|
+
"#{@fs.display_path}#{$terminal.color(@persist_ruby ? '~' : '>', :yellow)} "
|
127
127
|
end
|
128
128
|
|
129
129
|
def introspect_object obj
|
@@ -204,10 +204,12 @@ class RubyEvaluator
|
|
204
204
|
if a.empty?
|
205
205
|
if MODULES.member? str
|
206
206
|
MODULES[str]
|
207
|
-
elsif @fs.marks
|
208
|
-
|
209
|
-
|
210
|
-
|
207
|
+
elsif str =~ /_?([\w\d]+)(!?)/ && objs = @fs.marks[$1]
|
208
|
+
if $2 == '!'
|
209
|
+
objs
|
210
|
+
else
|
211
|
+
objs.first
|
212
|
+
end
|
211
213
|
else
|
212
214
|
super
|
213
215
|
end
|
data/lib/rvc/util.rb
CHANGED
@@ -93,6 +93,16 @@ module Util
|
|
93
93
|
progress(objs.map { |obj| obj._call :"#{sym}_Task", args })
|
94
94
|
end
|
95
95
|
|
96
|
+
if ENV['LANG'] =~ /UTF/ and RUBY_VERSION >= '1.9.1'
|
97
|
+
PROGRESS_BAR_LEFT = "\u2772"
|
98
|
+
PROGRESS_BAR_MIDDLE = "\u25AC"
|
99
|
+
PROGRESS_BAR_RIGHT = "\u2773"
|
100
|
+
else
|
101
|
+
PROGRESS_BAR_LEFT = "["
|
102
|
+
PROGRESS_BAR_MIDDLE = "="
|
103
|
+
PROGRESS_BAR_RIGHT = "]"
|
104
|
+
end
|
105
|
+
|
96
106
|
def progress tasks
|
97
107
|
interested = %w(info.progress info.state info.entityName info.error info.name)
|
98
108
|
connection = single_connection tasks
|
@@ -106,7 +116,7 @@ module Util
|
|
106
116
|
progress = props['info.progress']
|
107
117
|
barlen = terminal_columns - text.size - 2
|
108
118
|
progresslen = ((progress||0)*barlen)/100
|
109
|
-
progress_bar = "
|
119
|
+
progress_bar = "#{PROGRESS_BAR_LEFT}#{PROGRESS_BAR_MIDDLE * progresslen}#{' ' * (barlen-progresslen)}#{PROGRESS_BAR_RIGHT}"
|
110
120
|
$stdout.write "\e[K#{text}#{progress_bar}\n"
|
111
121
|
elsif state == 'error'
|
112
122
|
error = props['info.error']
|
data/test/test_fs.rb
CHANGED
@@ -14,6 +14,8 @@ class FSTest < Test::Unit::TestCase
|
|
14
14
|
NodeA = FixtureNode.new('A', 'b' => NodeB, 'c' => NodeC)
|
15
15
|
Root = FixtureNode.new('ROOT', 'a' => NodeA, 'd' => NodeD)
|
16
16
|
|
17
|
+
Root.rvc_link nil, ''
|
18
|
+
|
17
19
|
def setup
|
18
20
|
@context = RVC::FS.new Root
|
19
21
|
end
|
@@ -26,8 +28,7 @@ class FSTest < Test::Unit::TestCase
|
|
26
28
|
assert_equal Root, @context.cur
|
27
29
|
assert_equal "", @context.display_path
|
28
30
|
assert_equal 0, @context.marks.size
|
29
|
-
assert_equal [''], @context.
|
30
|
-
assert_equal [['', Root]], @context.loc.stack
|
31
|
+
assert_equal [['', Root]], @context.cur.rvc_path
|
31
32
|
end
|
32
33
|
|
33
34
|
def test_lookup_simple
|
@@ -41,79 +42,79 @@ class FSTest < Test::Unit::TestCase
|
|
41
42
|
assert_equal [NodeC], @context.lookup('a/b/.../c')
|
42
43
|
end
|
43
44
|
|
44
|
-
def
|
45
|
-
|
46
|
-
assert_equal [],
|
45
|
+
def test_lookup_nonexistent
|
46
|
+
objs = @context.lookup 'nonexistent'
|
47
|
+
assert_equal [], objs
|
47
48
|
end
|
48
49
|
|
49
|
-
def
|
50
|
+
def test_lookup_simple_path
|
50
51
|
%w(a /a ./a ./a/.).each do |path|
|
51
|
-
|
52
|
-
assert_equal NodeA,
|
53
|
-
assert_equal ['', 'a'],
|
54
|
-
assert_equal [['', Root], ['a', NodeA]], loc.stack
|
52
|
+
obj = @context.lookup(path)[0]
|
53
|
+
assert_equal NodeA, obj
|
54
|
+
assert_equal [['', Root], ['a', NodeA]], obj.rvc_path
|
55
55
|
end
|
56
56
|
|
57
57
|
%w(a/b /a/b ./a/b /a/b/.).each do |path|
|
58
|
-
|
59
|
-
assert_equal NodeB,
|
60
|
-
assert_equal ['', 'a', 'b'],
|
61
|
-
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], loc.stack
|
58
|
+
obj = @context.lookup(path)[0]
|
59
|
+
assert_equal NodeB, obj
|
60
|
+
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], obj.rvc_path
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
65
|
-
def
|
66
|
-
|
67
|
-
assert_equal [['', Root]],
|
64
|
+
def test_lookup_parent
|
65
|
+
obj = @context.lookup('..')[0]
|
66
|
+
assert_equal [['', Root]], obj.rvc_path
|
68
67
|
|
69
|
-
|
70
|
-
assert_equal [['', Root]],
|
68
|
+
obj = @context.lookup('a/..')[0]
|
69
|
+
assert_equal [['', Root]], obj.rvc_path
|
71
70
|
|
72
|
-
|
73
|
-
assert_equal [['', Root], ['a', NodeA]],
|
71
|
+
obj = @context.lookup('a/b/..')[0]
|
72
|
+
assert_equal [['', Root], ['a', NodeA]], obj.rvc_path
|
74
73
|
end
|
75
74
|
|
75
|
+
=begin
|
76
76
|
def test_lookup_loc_realparent
|
77
|
-
|
78
|
-
assert_equal [['', Root]],
|
77
|
+
obj = @context.lookup('...')[0]
|
78
|
+
assert_equal [['', Root]], obj.rvc_path
|
79
79
|
|
80
|
-
|
81
|
-
assert_equal [['', Root], ['a', NodeA], ['...', Root]],
|
80
|
+
obj = @context.lookup('a/...')[0]
|
81
|
+
assert_equal [['', Root], ['a', NodeA], ['...', Root]], obj.rvc_path
|
82
82
|
|
83
|
-
|
84
|
-
assert_equal [['', Root], ['a', NodeA], ['b', NodeB], ['...', NodeA]],
|
83
|
+
obj = @context.lookup('a/b/...')[0]
|
84
|
+
assert_equal [['', Root], ['a', NodeA], ['b', NodeB], ['...', NodeA]], obj.rvc_path
|
85
85
|
end
|
86
|
+
=end
|
86
87
|
|
87
|
-
def
|
88
|
-
|
89
|
-
assert_not_nil
|
88
|
+
def test_lookup_mark
|
89
|
+
b_obj = @context.lookup('a/b')[0]
|
90
|
+
assert_not_nil b_obj
|
90
91
|
|
91
|
-
|
92
|
-
assert_equal nil,
|
92
|
+
obj = @context.lookup('~foo')[0]
|
93
|
+
assert_equal nil, obj
|
93
94
|
|
94
95
|
['foo', '~', '7', ''].each do |mark|
|
95
|
-
@context.mark mark,
|
96
|
-
|
97
|
-
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]],
|
96
|
+
@context.mark mark, [b_obj]
|
97
|
+
obj = @context.lookup("~#{mark}")[0]
|
98
|
+
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], obj.rvc_path
|
98
99
|
|
99
|
-
@context.mark mark,
|
100
|
-
|
101
|
-
assert_equal nil,
|
100
|
+
@context.mark mark, []
|
101
|
+
obj = @context.lookup("~#{mark}")[0]
|
102
|
+
assert_equal nil, obj
|
102
103
|
end
|
103
104
|
|
104
|
-
@context.mark '7',
|
105
|
-
|
106
|
-
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]],
|
105
|
+
@context.mark '7', [b_obj]
|
106
|
+
obj = @context.lookup("7")[0]
|
107
|
+
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], obj.rvc_path
|
107
108
|
|
108
|
-
@context.mark '7',
|
109
|
-
|
110
|
-
assert_equal nil,
|
109
|
+
@context.mark '7', []
|
110
|
+
obj = @context.lookup("7")[0]
|
111
|
+
assert_equal nil, obj
|
111
112
|
end
|
112
113
|
|
113
114
|
def test_cd
|
114
|
-
assert_equal [['', Root]], @context.
|
115
|
-
@context.cd(@context.
|
116
|
-
assert_equal [['', Root], ['a', NodeA]], @context.
|
115
|
+
assert_equal [['', Root]], @context.cur.rvc_path
|
116
|
+
@context.cd(@context.lookup("a")[0])
|
117
|
+
assert_equal [['', Root], ['a', NodeA]], @context.cur.rvc_path
|
117
118
|
end
|
118
119
|
|
119
120
|
def test_regex
|
@@ -121,12 +122,12 @@ class FSTest < Test::Unit::TestCase
|
|
121
122
|
dab = [['', Root], ['d', NodeD], ['dab', NodeDab]]
|
122
123
|
dabc = [['', Root], ['d', NodeD], ['dabc', NodeDabc]]
|
123
124
|
dac = [['', Root], ['d', NodeD], ['dac', NodeDac]]
|
124
|
-
|
125
|
-
assert_equal [daa],
|
126
|
-
|
127
|
-
assert_equal [daa],
|
128
|
-
|
129
|
-
assert_equal [dabc, dac],
|
125
|
+
objs = @context.lookup '/d/%^daa'
|
126
|
+
assert_equal [daa], objs.map(&:rvc_path)
|
127
|
+
objs = @context.lookup '/d/%^daa.*'
|
128
|
+
assert_equal [daa], objs.map(&:rvc_path)
|
129
|
+
objs = @context.lookup '/d/%^da.*c'
|
130
|
+
assert_equal [dabc, dac], objs.map(&:rvc_path)
|
130
131
|
end
|
131
132
|
|
132
133
|
def test_glob
|
@@ -134,11 +135,11 @@ class FSTest < Test::Unit::TestCase
|
|
134
135
|
dab = [['', Root], ['d', NodeD], ['dab', NodeDab]]
|
135
136
|
dabc = [['', Root], ['d', NodeD], ['dabc', NodeDabc]]
|
136
137
|
dac = [['', Root], ['d', NodeD], ['dac', NodeDac]]
|
137
|
-
|
138
|
-
assert_equal [daa],
|
139
|
-
|
140
|
-
assert_equal [daa],
|
141
|
-
|
142
|
-
assert_equal [dabc, dac],
|
138
|
+
objs = @context.lookup '/d/*daa*'
|
139
|
+
assert_equal [daa], objs.map(&:rvc_path)
|
140
|
+
objs = @context.lookup '/d/d*a'
|
141
|
+
assert_equal [daa], objs.map(&:rvc_path)
|
142
|
+
objs = @context.lookup '/d/da*c'
|
143
|
+
assert_equal [dabc, dac], objs.map(&:rvc_path)
|
143
144
|
end
|
144
145
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rvc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.
|
5
|
+
version: 1.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Rich Lane
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
13
|
+
date: 2011-04-19 00:00:00 -07:00
|
14
14
|
default_executable: rvc
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -79,12 +79,14 @@ files:
|
|
79
79
|
- lib/rvc/extensions/VirtualMachine.rb
|
80
80
|
- lib/rvc/fs.rb
|
81
81
|
- lib/rvc/inventory.rb
|
82
|
+
- lib/rvc/known_hosts.rb
|
82
83
|
- lib/rvc/modules.rb
|
83
84
|
- lib/rvc/modules/basic.rb
|
84
85
|
- lib/rvc/modules/cluster.rb
|
85
86
|
- lib/rvc/modules/datacenter.rb
|
86
87
|
- lib/rvc/modules/datastore.rb
|
87
88
|
- lib/rvc/modules/host.rb
|
89
|
+
- lib/rvc/modules/mark.rb
|
88
90
|
- lib/rvc/modules/resource_pool.rb
|
89
91
|
- lib/rvc/modules/vim.rb
|
90
92
|
- lib/rvc/modules/vm.rb
|