vi_cli 0.1.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.
data/History.txt ADDED
@@ -0,0 +1,12 @@
1
+ === 0.1.0 / 2009-04-26
2
+
3
+ * First Public Release
4
+ * MVC Design
5
+ * Threaded ncurses interface
6
+ * Basic VMware Virtual Infrastructure client functionality
7
+ * Virtual Machines and folders as tree view
8
+ * Virtual Machine overview/summary
9
+ * Virtual Machine hardware
10
+ * Current events
11
+ * Start/Stop/Reboot Virtual Machine
12
+
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ bin/vi_cli
5
+ lib/Mock.rb
6
+ lib/VM.rb
7
+ lib/VMFolder.rb
8
+ lib/VMTreeItemRenderer.rb
9
+ lib/VcData.rb
10
+ lib/ViCli.rb
11
+ lib/ViCliConfig.rb
12
+ lib/ViGUI.rb
13
+ lib/Watcher.rb
14
+ rakefile
data/README.txt ADDED
@@ -0,0 +1,58 @@
1
+ = vi_cli
2
+
3
+ http://rubyforge.org/projects/vi-cli/
4
+
5
+ == DESCRIPTION:
6
+
7
+ vi_cli is an ncurses based commandline client for VMware ESX and ESXi servers
8
+
9
+ == FEATURES:
10
+
11
+ * Virtual Machines and folders as tree view
12
+ * Virtual Machine overview/summary
13
+ * Virtual Machine hardware
14
+ * Current events
15
+ * Start/Stop/Reboot Virtual Machine
16
+
17
+ == PROBLEMS/BUGS:
18
+
19
+ * Many ideas for improvements already on the imaginary todo list
20
+
21
+ == USAGE:
22
+
23
+ Just execute the executable!
24
+ Type q to quit ;)
25
+
26
+ == REQUIREMENTS:
27
+
28
+ * rbcurses
29
+ * vi_api_lib
30
+
31
+ == INSTALL:
32
+
33
+ * sudo gem install vi_cli
34
+
35
+ == LICENSE:
36
+
37
+ (The MIT License)
38
+
39
+ Copyright (c) 2009 SebDE
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining
42
+ a copy of this software and associated documentation files (the
43
+ 'Software'), to deal in the Software without restriction, including
44
+ without limitation the rights to use, copy, modify, merge, publish,
45
+ distribute, sublicense, and/or sell copies of the Software, and to
46
+ permit persons to whom the Software is furnished to do so, subject to
47
+ the following conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
55
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
56
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
57
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
58
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/vi_cli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ViCli.rb'
4
+
5
+ ViCli.new.main
6
+
data/lib/Mock.rb ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A class to replace all vmware data structures with random text if vmware lib not available
4
+ class Mock < String
5
+ # initializes self with a random string
6
+ def initialize
7
+ # methods that just need to be there to show the task buttons
8
+ @methods = [:powerOnVM_Task,:shutdownGuest,:powerOffVM,:markAsVirtualMachine,:markAsTemplate,:suspendVM_Task,:resetVM_Task,:rebootGuest,:migrateVM_Task]
9
+ res = ''
10
+ -3.upto(rand 8) do
11
+ res << (65 + rand(57))
12
+ end
13
+ super res
14
+ end
15
+ # returns a new instance of it's own class
16
+ def [] i=nil
17
+ self.class.new
18
+ end
19
+ alias :task_manager :[]
20
+ alias :recent_tasks :[]
21
+ # expand the list of methods this class responds to
22
+ def respond_to? sym
23
+ return true if @methods.include? sym.to_sym
24
+ super
25
+ end
26
+ # fake if a vmware data structure should respond to this method
27
+ def method_missing sym, *args
28
+ return if @methods.include? sym.to_sym
29
+ super
30
+ end
31
+ # returns something VcData interprets as device
32
+ def soap_class
33
+ 'VirtualDisk'
34
+ end
35
+ end
data/lib/VM.rb ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ class VM
4
+ def initialize ref, session, name
5
+ @ref = ref
6
+ @session = session
7
+ @name = name
8
+ @state_vars = ['vm_on','vm_off','is_templ']
9
+ @state_vars.map! {|i| "@"+i+"=nil" }
10
+ @color = 'grey'
11
+ end
12
+ def name
13
+ @name.strip
14
+ end
15
+ def refresh_state
16
+ @color = 'grey'
17
+ @state_vars.each {|i| self.instance_eval i } # XXX
18
+ end
19
+ def draw
20
+ yield [@name, self]
21
+ end
22
+ def color cached=nil
23
+ return @color if cached
24
+ @color = is_templ ? 'blue' : vm_on ? 'green' : vm_off ? 'red' : 'yellow'
25
+ end
26
+ def vm_on
27
+ return @vm_on if !@vm_on.nil?
28
+ @vm_on = @ref['runtime']['powerState'].to_s == 'poweredOn'
29
+ end
30
+ def vm_off
31
+ return @vm_off if !@vm_off.nil?
32
+ @vm_off = @ref['runtime']['powerState'].to_s == 'poweredOff'
33
+ end
34
+ def is_templ
35
+ return @is_templ if !@is_templ.nil?
36
+ @is_templ = @ref['summary']['config']['template']
37
+ end
38
+ def has_task? task
39
+ @ref.respond_to? task
40
+ end
41
+ def get_host
42
+ host = @ref['runtime']['host']
43
+ return '' if host.nil? or host.kind_of? $mock.class
44
+ host = @session.managed_object_wrapper_factory "HostSystem", host
45
+ @host_cpu = host['summary']['hardware']['cpuMhz']
46
+ host['name']
47
+ end
48
+ def get_hw_info
49
+ @ref['config']['hardware']['device'].collect do |d|
50
+ if d.soap_type == 'VirtualCdrom'
51
+ result = [d['deviceInfo']['label'].to_s,d['backing']['fileName'].to_s]
52
+ end
53
+ if d.soap_type == 'VirtualDisk'
54
+ i = d['deviceInfo']
55
+ result = [i['label'].to_s,i['summary'].to_s]
56
+ end
57
+ result unless result.nil?
58
+ end
59
+ end
60
+ def state
61
+ snapshot = @ref['snapshot']
62
+ snapshot = snapshot['currentSnapshot'].nil? ? 'no' : 'yes' unless snapshot.nil?
63
+ runtime = @ref['runtime']
64
+ summary = @ref['summary']
65
+ config = @ref['config']
66
+ num = summary['config']['numCpu'].to_s
67
+ mem = summary['config']['memorySizeMB'].to_s
68
+ qs = summary['quickStats']
69
+ guest = @ref['guest']
70
+ res = []
71
+ res << ['power state',runtime['powerState'].to_s+' since '+runtime['bootTime'].to_s]
72
+ res << ['host',get_host]
73
+ res << ['cpu usage',"#{qs['overallCpuUsage'].to_s} MHz / #{num} * #{@host_cpu.to_s} MHz"]
74
+ res << ['guest memory usage',"#{qs['guestMemoryUsage'].to_s} MB / #{mem} MB"]
75
+ res << ['host memory usage',qs['hostMemoryUsage'].to_s+' MB']
76
+ res << ['memory overhead',(runtime['memoryOverhead'].to_i/1024/1024).to_s+' MB']
77
+ res << ['console',runtime['numMksConnections'].to_s]
78
+ res << ['snapshot',snapshot.to_s]
79
+ unless guest.nil?
80
+ res << ['host name',guest['hostName'].to_s]
81
+ res << ['ip',guest['ipAddress'].to_s]
82
+ res << ['tools version',guest['toolsVersion'].to_s]
83
+ begin
84
+ guest['disk'].each do |d|
85
+ size = d['capacity']
86
+ pc = d['freeSpace']*100/size
87
+ size = size/1024/1024/1024
88
+ res << [d['diskPath'], "#{size.round.to_s}GB #{pc.round.to_s}% free"]
89
+ end unless guest['disk'].nil?
90
+ rescue => ex
91
+ res << ['exception',ex.to_s]
92
+ end
93
+ end
94
+ res << ['store',config['files']['vmPathName'].to_s]
95
+ first = true
96
+ config['annotation'].to_s.split(/\n/).each do |a|
97
+ res << [first ? 'notes' : '',a]
98
+ first = false
99
+ end
100
+ [res, get_hw_info.compact]
101
+ end
102
+ def execute_task task
103
+ @ref.send task.to_sym
104
+ false
105
+ end
106
+ end
107
+
data/lib/VMFolder.rb ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ class VMFolder
4
+ attr_reader :collapsed
5
+ def initialize ref, session, name, collapsed = false
6
+ @ref = ref
7
+ @session = session
8
+ @name = name
9
+ @children = []
10
+ @collapsed = collapsed
11
+ end
12
+ def name
13
+ @name.strip
14
+ end
15
+ def save_data
16
+ result = [@collapsed]
17
+ @children.each { |c| result << c.save_data if c.respond_to? :save_data }
18
+ result
19
+ end
20
+ def add_child child
21
+ @children.push child
22
+ end
23
+ def has_task? task
24
+ ['fold', 'unfold','toggle_fold','fold_children'].include? task.to_s
25
+ end
26
+ def state
27
+ res = [[],[]]
28
+ end
29
+ def execute_task task
30
+ self.send task.to_sym if has_task? task
31
+ true
32
+ end
33
+ def fold
34
+ @collapsed = true
35
+ end
36
+ def unfold
37
+ @collapsed = false
38
+ end
39
+ def toggle_fold
40
+ @collapsed = !@collapsed
41
+ end
42
+ def fold_children
43
+ @children.each { |c| c.fold if c.respond_to? :fold }
44
+ end
45
+ def draw
46
+ yield [@name, self]
47
+ unless @collapsed
48
+ @children.each { |c| c.draw { |i| yield i } }
49
+ end
50
+ end
51
+ def color cached=nil
52
+ @collapsed ? 'grey' : 'white'
53
+ end
54
+ end
55
+
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ class VMTreeItemRenderer
4
+ attr_accessor :display_length
5
+
6
+ def initialize parent, color_callback, w=nil
7
+ @parent = parent
8
+ @color_callback = color_callback
9
+ @display_length = w || 10
10
+ end
11
+ def repaint graphic, r=nil,c=nil, idx=-1,value='', focussed=false, selected=false
12
+ return if r.nil? or c.nil? or idx == -1
13
+ attr = 'bold'
14
+ bgcolor = focussed ? 'yellow' : 'black'
15
+ fgcolor = @color_callback.call idx
16
+ if fgcolor == 'grey'
17
+ attr = nil
18
+ fgcolor = 'white'
19
+ end
20
+ color_pair = ColorMap.get_color fgcolor, bgcolor
21
+ # ensure we do not exceed
22
+ if !@display_length.nil?
23
+ value = value[0..@display_length-1] if value.length > @display_length
24
+ end
25
+ len = @display_length || value.length
26
+ graphic.printstring r, c, '%-*s' % [len, value], color_pair, attr
27
+ end
28
+ end
29
+
data/lib/VcData.rb ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'VM'
4
+ require 'VMFolder'
5
+
6
+ begin
7
+ require 'vi_api_lib'
8
+ rescue LoadError => ex
9
+ require 'Mock'
10
+ $mock = Mock.new
11
+ end
12
+
13
+ class VcData
14
+ attr_reader :tree_changed
15
+ def initialize login, save_data
16
+ # get rid of certificate warning, why does it have to be on stdout anyway?
17
+ $stderr.reopen '/dev/null', 'w'
18
+ url = "https://#{login['host']}/sdk"
19
+ @connection = $mock || VMware::Connection.new(url, login['username'], login['password'], false)
20
+ @session = $mock || VMware::Session.new(@connection)
21
+ @vm_folder = $mock || @session.root_folder.children[0].vm_folder
22
+ @refs = []
23
+ @save_data = save_data
24
+ @tree_changed = false
25
+ end
26
+ def collect folder, save_data, indent=''
27
+ if folder.class.to_s == "VMware::Folder"
28
+ save_data = [] if !save_data.kind_of? Array
29
+ i = 0
30
+ result = VMFolder.new folder, @session, indent+folder['name'], save_data[0] == true
31
+ folder.children.each { |c| i+=1; result.add_child collect c, save_data[i], indent+' ' }
32
+ return result
33
+ end
34
+ if folder.kind_of? $mock.class and indent == ''
35
+ res = VMFolder.new folder, @session, indent+folder['name']
36
+ res.add_child collect $mock.class.new, [], ' '
37
+ res.add_child collect $mock.class.new, [], ' '
38
+ return res
39
+ end
40
+ VM.new folder, @session, indent+folder['name']
41
+ end
42
+ def get sym, index
43
+ i = @refs[index]
44
+ i.send sym if i.respond_to? sym
45
+ end
46
+ def execute_task index, task
47
+ @tree_changed = @refs[index].execute_task task
48
+ end
49
+ def get_tasks index, tasks
50
+ tasks.keys.collect { |t| (@refs[index].has_task? tasks[t]) ? t : nil }.compact
51
+ end
52
+ def get_colors cached=nil
53
+ @refs.collect {|i| i.color cached }
54
+ end
55
+ def refresh_list dummy=nil
56
+ @save_data = @folders.save_data if !@folders.nil?
57
+ @folders = collect @vm_folder, @save_data
58
+ end
59
+ def refresh_state dummy=nil
60
+ @refs.each {|i| i.refresh_state if i.respond_to? :refresh_state }
61
+ end
62
+ def vm_list
63
+ @refs.clear
64
+ list = []
65
+ @folders.draw { |i| list << i[0]; @refs << i[1] }
66
+ list
67
+ end
68
+ def save_data
69
+ @folders.save_data
70
+ end
71
+ def recent_events
72
+ @session.task_manager.recent_tasks.collect do |t|
73
+ desc = t['name'] || t['descriptionId'] # TODO use task descriptions form TaskManager
74
+ target = t['entityName']
75
+ cancelled = t['cancelled'].to_s
76
+ status = t['state']
77
+ status = t['error']['localizedMessage'] if status == 'error'
78
+ user = t['reason']
79
+ user = user['userName'] || user['name'] || user['alarmName'] || '<System>'
80
+ start = t['startTime']
81
+ queue = t['queueTime']
82
+ complete = t['completeTime']
83
+ [desc,target,cancelled,status,user,start.to_s]
84
+ end
85
+ end
86
+ end
87
+
data/lib/ViCli.rb ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ViCliConfig'
4
+ require 'ViGUI'
5
+ require 'VcData'
6
+ require 'Watcher'
7
+
8
+ class ViCli
9
+ def main
10
+ begin
11
+ @gui = ViGUI.new
12
+ @config = ViCliConfig.new
13
+ @config.login = @gui.enter_login if @config.login_needed?
14
+ @watcher = Watcher.new
15
+ @model = VcData.new @config.login, @config.tree_state
16
+ @gui.setup self.method(:on_focus_callback)
17
+ @gui.set_status "Loading VM Folders ..."
18
+ @model.refresh_list
19
+ @gui.set_status "Loaded VM Folders"
20
+ draw_list
21
+ on_focus_callback @gui.current_list_index
22
+ setup_queues
23
+ main_loop
24
+ @config.tree_state = @model.save_data
25
+ rescue => ex
26
+ ensure
27
+ @gui.unload if !@gui.nil?
28
+ puts ex if ex
29
+ print ex.backtrace.join "\n" if ex
30
+ end
31
+ end
32
+ def draw_list index=nil
33
+ @gui.draw_list @model.vm_list
34
+ @gui.colors = @model.get_colors true
35
+ @watcher.refresh_queue.push index unless index.nil?
36
+ end
37
+ def on_focus_callback index
38
+ tasks = @model.get_tasks index, @config.keys
39
+ callback = self.method :on_fire_callback
40
+ @gui.add_tasks callback, index, tasks
41
+ @watcher.refresh_queue.push index
42
+ @gui.refresh
43
+ end
44
+ def on_fire_callback index, key
45
+ redraw = @model.execute_task index, @config.keys[key]
46
+ @gui.set_status "execute #{@config.keys[key]} on #{@model.get :name, index}"
47
+ @gui.current_list_index
48
+ draw_list index if redraw
49
+ end
50
+ def handle_char char
51
+ return false if char.nil?
52
+ return true if char == ?q
53
+ @activity = true
54
+ @gui.enter_login if char == ?l
55
+ @model.refresh_list @gui.current_list_index if char == 269
56
+ @gui.draw_list @model.vm_list if [269,360].include? char
57
+ @gui.handle_key char
58
+ @gui.refresh
59
+ false
60
+ end
61
+ def draw_state state
62
+ return if state.nil?
63
+ @gui.colors = state[2]
64
+ @gui.add_hardware state[1]
65
+ @gui.add_state state[0]
66
+ @gui.refresh
67
+ end
68
+ def refresh x
69
+ return if x.nil?
70
+ @gui.draw_events @model.recent_events
71
+ @gui.refresh
72
+ @model.refresh_state
73
+ @watcher.refresh_queue.push @gui.current_list_index
74
+ return @activity = false if @activity
75
+ @gui.set_status ''
76
+ end
77
+ def setup_queues
78
+ #@watcher.register(:resize) { |q| loop { q.push sleep 5 } }
79
+ @watcher.register(:getchar) { |q| loop { q.push @gui.getchar } }
80
+ @watcher.register(:sleep) { |q| loop { q.push sleep 5 } }
81
+ @watcher.register(:state) do |q|
82
+ loop do
83
+ q.push @model.get(:state, @watcher.refresh_queue.poop) << @model.get_colors
84
+ end
85
+ end
86
+ end
87
+ def main_loop
88
+ while @watcher.wait
89
+ return if handle_char @watcher[:getchar].poop
90
+ draw_state @watcher[:state].poop
91
+ refresh @watcher[:sleep].poop
92
+ end
93
+ end
94
+ end
95
+
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+
5
+ class ViCliConfig
6
+ def initialize
7
+ @config_file = File.expand_path "~/.vi_cli"
8
+ @config = YAML.load_file @config_file
9
+ end
10
+ def tree_state
11
+ @config['tree_state']
12
+ end
13
+ def tree_state= state
14
+ @config['tree_state'] = state
15
+ save
16
+ end
17
+ def login
18
+ @config['login']
19
+ end
20
+ def login= login
21
+ @config['login'] = login
22
+ end
23
+ def login_needed?
24
+ @config['login'].nil?
25
+ end
26
+ def keys
27
+ return @config['keys'] unless @config['keys'].nil?
28
+ @config['keys'] = {
29
+ '&power on' => 'powerOnVM_Task',
30
+ '&unfold' => 'unfold',
31
+ '&reboot guest' => 'rebootGuest',
32
+ 'convert to &virtual machine' => 'markAsVirtualMachine', # doesn't work
33
+ 'cut power' => 'powerOffVM',
34
+ '&migrate' => 'migrateVM_Task', # doesn't work
35
+ 'f&old' => 'fold',
36
+ 'fold &children' => 'fold_children',
37
+ 'convert to &template' => 'markAsTemplate',
38
+ 'suspend' => 'suspendVM_Task',
39
+ 'reset' => 'resetVM_Task',
40
+ '&shutdown guest' => 'shutdownGuest',
41
+ }
42
+ end
43
+ def save
44
+ File.open(@config_file,'w').puts @config.to_yaml
45
+ end
46
+ end
data/lib/ViGUI.rb ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'VMTreeItemRenderer'
4
+ require 'ncurses'
5
+ require 'rbcurse'
6
+ require 'rbcurse/rlistbox'
7
+ require 'rbcurse/rtextarea'
8
+ require 'rbcurse/rtable'
9
+ require 'rbcurse/rmessagebox'
10
+ include RubyCurses
11
+
12
+ class ViGUI
13
+ def initialize
14
+ $log = Log.new # WHY??? XXX
15
+ VER::start_ncurses
16
+ @window = VER::Window.root_window
17
+ @form = Form.new @window
18
+ @current_list_index = 0
19
+ @current_list_toprow = 0
20
+ @colors = []
21
+ end
22
+ def enter_login
23
+ dialog = Form.new nil
24
+ inputs = ['host','username','password']
25
+ inputs.each_with_index do |text,i|
26
+ Label.new dialog do
27
+ col 4
28
+ row 4 + i*2
29
+ text text+":"
30
+ attr 'reverse'
31
+ end
32
+ Field.new dialog do
33
+ name text
34
+ col 14
35
+ row 4 + i*2
36
+ display_length 41
37
+ maxlen 99
38
+ end
39
+ end
40
+ h, w = get_h_w
41
+ MessageBox.new dialog do
42
+ title 'Login Configuration'
43
+ layout 14, 60, h/2 - 7, w/2 - 30
44
+ end
45
+ Hash[*inputs.collect{|i| [i,dialog.by_name[i].getvalue]}.flatten]
46
+ end
47
+ def get_h_w
48
+ h = []
49
+ w = []
50
+ Ncurses.getmaxyx Ncurses.stdscr, h, w
51
+ return h[0] -2, w[0]
52
+ end
53
+ def setup focus_callback
54
+ @tw = tw = 40
55
+ h, w = get_h_w
56
+ h1 = (h - 1)/3
57
+ h2 = h/4 - 2
58
+ h3 = h/4 + 1
59
+ h4 = h - h1 - h2 - h3 - 4
60
+ @h1 = h1 + h2
61
+ create_vmlist 0, 0, h, tw
62
+ @overview = create_widget Table, 'overview', 'Overview', 0, tw, h1, w - tw
63
+ create_overview w - tw
64
+ @hardware = create_widget Table, 'hardware', 'Hardware', h1 + 1, tw, h2, w - tw
65
+ create_hardware w - tw
66
+ @tasks = create_widget TextArea, 'tasks', 'Tasks', h1 + h2 + 2, tw, h3, w - tw
67
+ @events = create_widget Table, 'events', 'Events', h1 + h2 + h3 + 3, tw, h4, w - tw
68
+ create_events w - tw
69
+ create_status h, tw, 1, w - tw
70
+ refresh
71
+ @tree.bind(:ENTER_ROW, focus_callback) do |tree,callback|
72
+ focus_callback.call tree.current_index
73
+ end
74
+ end
75
+ def add_tasks callback, index, tasks
76
+ col = @tw + 1
77
+ row = @h1 + 3
78
+ # move to a button list class TODO
79
+ w = @form.widgets.collect { |w| w.class == Button ? w : nil }
80
+ w.compact.each { |w| @form.remove_widget w }
81
+ @tasks.paint
82
+ tasks.each do |task|
83
+ b = Button.new @form do
84
+ col col
85
+ row row
86
+ text task
87
+ end
88
+ b.command { |form| callback.call index, task }
89
+ row += 1
90
+ end
91
+ end
92
+ def add_state state
93
+ @overview.set_data state, @overview.table_column_model
94
+ end
95
+ def add_hardware hardware
96
+ @hardware.set_data hardware, @hardware.table_column_model
97
+ end
98
+ def draw_events events
99
+ @events.set_data events, @events.table_column_model
100
+ end
101
+ def color_callback index
102
+ @colors[index] || 'grey'
103
+ end
104
+ def create_vmlist row, col, h, w
105
+ renderer = VMTreeItemRenderer.new self, self.method(:color_callback), w-2
106
+ @tree = Listbox.new @form do
107
+ name 'vmlist'
108
+ title 'VMs'
109
+ row row
110
+ col col
111
+ height h
112
+ width w
113
+ list_variable Variable.new []
114
+ cell_renderer renderer
115
+ end
116
+ end
117
+ def create_overview w
118
+ w1 = w / 2 - w / 4
119
+ colnames = DefaultTableColumnModel.new %w[key value]
120
+ colnames.column(0).width w1
121
+ colnames.column(1).width w - w1 - 3
122
+ @overview.set_data [], colnames
123
+ end
124
+ def create_hardware w
125
+ w1 = w / 2 - w / 4
126
+ colnames = DefaultTableColumnModel.new %w[device summary]
127
+ colnames.column(0).width w1
128
+ colnames.column(1).width w - w1 - 3
129
+ @hardware.set_data [], colnames
130
+ end
131
+ def create_events w
132
+ colnames = DefaultTableColumnModel.new %w[name target canceled status user start]
133
+ @events.set_data [], colnames
134
+ end
135
+ def create_widget class_name, name, title, row, col, h, w
136
+ class_name.new @form do
137
+ name name
138
+ title title
139
+ row row
140
+ col col
141
+ height h
142
+ width w
143
+ focusable false
144
+ end
145
+ end
146
+ def create_status row, col, h, w
147
+ @status = Label.new @form do
148
+ name 'status'
149
+ row row
150
+ col col
151
+ height h
152
+ display_length w
153
+ color 'white'
154
+ bgcolor 'blue'
155
+ attr 'bold'
156
+ end
157
+ end
158
+ def current_list_index
159
+ @current_list_toprow = @tree.toprow
160
+ @current_list_index = @tree.current_index
161
+ end
162
+ def unload
163
+ @window.destroy if !@window.nil?
164
+ VER::stop_ncurses
165
+ end
166
+ def set_status text
167
+ @status.text = " "+text
168
+ refresh
169
+ end
170
+ def getchar
171
+ @window.getchar
172
+ end
173
+ def handle_key ch
174
+ @form.handle_key ch
175
+ end
176
+ def refresh
177
+ @form.repaint
178
+ end
179
+ def draw_list list
180
+ @tree.list_data_model.remove_all
181
+ list.each { |i| @tree.list_data_model.append i }
182
+ @tree.current_index = @current_list_index
183
+ @tree.instance_eval "@toprow = #{@current_list_toprow}" # adapt library XXX
184
+ refresh
185
+ end
186
+ def colors= colors
187
+ @colors = colors
188
+ @tree.instance_eval "@repaint_required = true" # XXX
189
+ end
190
+ end
191
+
192
+ class Log
193
+ def debug x
194
+ end
195
+ end
data/lib/Watcher.rb ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thread'
4
+ require 'observer'
5
+
6
+ class Watcher
7
+ attr_reader :refresh_queue
8
+ def initialize
9
+ @queue = Queue.new
10
+ @queues = {}
11
+ @refresh_queue = LastQueue.new
12
+ end
13
+ def update
14
+ @queue.push true
15
+ end
16
+ def wait
17
+ @queue.pop
18
+ end
19
+ def [] key
20
+ @queues[key.to_s]
21
+ end
22
+ def register name, &block
23
+ q = @queues[name.to_s] = ObservedQueue.new(self)
24
+ Thread.new q, &block
25
+ end
26
+ end
27
+
28
+ class ObservedQueue < Queue
29
+ include Observable
30
+ def initialize observer
31
+ super()
32
+ add_observer observer
33
+ end
34
+ def push *args
35
+ super
36
+ changed
37
+ notify_observers
38
+ end
39
+ def poop
40
+ return nil if empty?
41
+ pop
42
+ end
43
+ end
44
+
45
+ class LastQueue < Queue
46
+ def poop
47
+ while !empty?
48
+ ret = pop
49
+ end
50
+ ret.nil? ? pop : ret
51
+ end
52
+ end
data/rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ version = File.open('History.txt').gets[/\d+\.\d+.\d+/]
7
+ Hoe.new('vi_cli', version) do |p|
8
+ p.developer('SebDE', 'v1g46nr02@sneakemail.com')
9
+ p.extra_deps = [['rbcurse','>= 0.1.2']]
10
+ p.extra_deps = [['vi_api_lib','>= 0.1.1']]
11
+ end
12
+
13
+ task :default => :clean
14
+
15
+ desc "make new history entry with increased major version number"
16
+ task 'make_major_history' do
17
+ make_history version, 0
18
+ end
19
+ desc "make new history entry with increased minor version number"
20
+ task 'make_minor_history' do
21
+ make_history version, 1
22
+ end
23
+ desc "make new history entry"
24
+ task 'make_history' do
25
+ make_history version, 2
26
+ end
27
+ def make_history version, x
28
+ v = version.split '.'
29
+ x.upto(2) do |i|
30
+ v[i] = i==x ? v[i].to_i + 1 : 0
31
+ end
32
+ history = File.open('History.txt').readlines
33
+ new_history = File.open 'History.txt', 'w'
34
+ new_history.puts "=== #{v.join '.'} / #{Time.new.strftime '%F'}"
35
+ new_history.puts "\n*\n\n"
36
+ new_history << history
37
+ end
38
+
39
+ # vim: syntaxx=Ruby
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vi_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SebDE
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-27 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: vi_api_lib
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.11.0
34
+ version:
35
+ description: vi_cli is an ncurses based commandline client for VMware ESX and ESXi servers
36
+ email:
37
+ - v1g46nr02@sneakemail.com
38
+ executables:
39
+ - vi_cli
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - README.txt
46
+ files:
47
+ - History.txt
48
+ - Manifest.txt
49
+ - README.txt
50
+ - bin/vi_cli
51
+ - lib/Mock.rb
52
+ - lib/VM.rb
53
+ - lib/VMFolder.rb
54
+ - lib/VMTreeItemRenderer.rb
55
+ - lib/VcData.rb
56
+ - lib/ViCli.rb
57
+ - lib/ViCliConfig.rb
58
+ - lib/ViGUI.rb
59
+ - lib/Watcher.rb
60
+ - rakefile
61
+ has_rdoc: true
62
+ homepage: http://rubyforge.org/projects/vi-cli/
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --main
66
+ - README.txt
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ requirements: []
82
+
83
+ rubyforge_project: vi_cli
84
+ rubygems_version: 1.2.0
85
+ signing_key:
86
+ specification_version: 2
87
+ summary: vi_cli is an ncurses based commandline client for VMware ESX and ESXi servers
88
+ test_files: []
89
+