vi_cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+