openxenmanager/0000755000175000017500000000000011643117607012271 5ustar rrsrrsopenxenmanager/xdot.py0000644000175000017500000025460111636446664013643 0ustar rrsrrs#!/usr/bin/env python # # Copyright 2008 Jose Fonseca # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # '''Visualize dot graphs via the xdot format.''' __author__ = "Jose Fonseca" __version__ = "0.4" import os import sys import subprocess import math import colorsys import time import re import gobject import gtk import gtk.gdk import gtk.keysyms import cairo import pango import pangocairo # See http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c # For pygtk inspiration and guidance see: # - http://mirageiv.berlios.de/ # - http://comix.sourceforge.net/ class Pen: """Store pen attributes.""" def __init__(self): # set default attributes self.color = (0.0, 0.0, 0.0, 1.0) self.fillcolor = (0.0, 0.0, 0.0, 1.0) self.linewidth = 1.0 self.fontsize = 14.0 self.fontname = "Times-Roman" self.dash = () def copy(self): """Create a copy of this pen.""" pen = Pen() pen.__dict__ = self.__dict__.copy() return pen def highlighted(self): pen = self.copy() pen.color = (1, 0, 0, 1) pen.fillcolor = (1, .8, .8, 1) return pen class Shape: """Abstract base class for all the drawing shapes.""" def __init__(self): pass def draw(self, cr, highlight=False): """Draw this shape with the given cairo context""" raise NotImplementedError def select_pen(self, highlight): if highlight: if not hasattr(self, 'highlight_pen'): self.highlight_pen = self.pen.highlighted() return self.highlight_pen else: return self.pen class TextShape(Shape): #fontmap = pangocairo.CairoFontMap() #fontmap.set_resolution(72) #context = fontmap.create_context() LEFT, CENTER, RIGHT = -1, 0, 1 def __init__(self, pen, x, y, j, w, t): Shape.__init__(self) self.pen = pen.copy() self.x = x self.y = y self.j = j self.w = w self.t = t def draw(self, cr, highlight=False): try: layout = self.layout except AttributeError: layout = cr.create_layout() # set font options # see http://lists.freedesktop.org/archives/cairo/2007-February/009688.html context = layout.get_context() fo = cairo.FontOptions() fo.set_antialias(cairo.ANTIALIAS_DEFAULT) fo.set_hint_style(cairo.HINT_STYLE_NONE) fo.set_hint_metrics(cairo.HINT_METRICS_OFF) try: pangocairo.context_set_font_options(context, fo) except TypeError: # XXX: Some broken pangocairo bindings show the error # 'TypeError: font_options must be a cairo.FontOptions or None' pass # set font font = pango.FontDescription() font.set_family(self.pen.fontname) font.set_absolute_size(self.pen.fontsize*pango.SCALE) layout.set_font_description(font) # set text layout.set_text(self.t) # cache it self.layout = layout else: cr.update_layout(layout) descent = 2 # XXX get descender from font metrics width, height = layout.get_size() width = float(width)/pango.SCALE height = float(height)/pango.SCALE # we know the width that dot thinks this text should have # we do not necessarily have a font with the same metrics # scale it so that the text fits inside its box if width > self.w: f = self.w / width width = self.w # equivalent to width *= f height *= f descent *= f else: f = 1.0 if self.j == self.LEFT: x = self.x elif self.j == self.CENTER: x = self.x - 0.5*width elif self.j == self.RIGHT: x = self.x - width else: assert 0 y = self.y - height + descent cr.move_to(x, y) cr.save() cr.scale(f, f) cr.set_source_rgba(*self.select_pen(highlight).color) cr.show_layout(layout) cr.restore() if 0: # DEBUG # show where dot thinks the text should appear cr.set_source_rgba(1, 0, 0, .9) if self.j == self.LEFT: x = self.x elif self.j == self.CENTER: x = self.x - 0.5*self.w elif self.j == self.RIGHT: x = self.x - self.w cr.move_to(x, self.y) cr.line_to(x+self.w, self.y) cr.stroke() class ImageShape(Shape): def __init__(self, pen, x0, y0, w, h, path): Shape.__init__(self) self.pen = pen.copy() self.x0 = x0 self.y0 = y0 self.w = w self.h = h self.path = path def draw(self, cr, highlight=False): cr2 = gtk.gdk.CairoContext(cr) pixbuf = gtk.gdk.pixbuf_new_from_file(self.path) cr2.set_source_pixbuf(pixbuf, self.x0, self.y0-self.h) cr2.paint() class EllipseShape(Shape): def __init__(self, pen, x0, y0, w, h, filled=False): Shape.__init__(self) self.pen = pen.copy() self.x0 = x0 self.y0 = y0 self.w = w self.h = h self.filled = filled def draw(self, cr, highlight=False): cr.save() cr.translate(self.x0, self.y0) cr.scale(self.w, self.h) cr.move_to(1.0, 0.0) cr.arc(0.0, 0.0, 1.0, 0, 2.0*math.pi) cr.restore() pen = self.select_pen(highlight) if self.filled: cr.set_source_rgba(*pen.fillcolor) cr.fill() else: cr.set_dash(pen.dash) cr.set_line_width(pen.linewidth) cr.set_source_rgba(*pen.color) cr.stroke() class PolygonShape(Shape): def __init__(self, pen, points, filled=False): Shape.__init__(self) self.pen = pen.copy() self.points = points self.filled = filled def draw(self, cr, highlight=False): x0, y0 = self.points[-1] cr.move_to(x0, y0) for x, y in self.points: cr.line_to(x, y) cr.close_path() pen = self.select_pen(highlight) if self.filled: cr.set_source_rgba(*pen.fillcolor) cr.fill_preserve() cr.fill() else: cr.set_dash(pen.dash) cr.set_line_width(pen.linewidth) cr.set_source_rgba(*pen.color) cr.stroke() class LineShape(Shape): def __init__(self, pen, points): Shape.__init__(self) self.pen = pen.copy() self.points = points def draw(self, cr, highlight=False): x0, y0 = self.points[0] cr.move_to(x0, y0) for x1, y1 in self.points[1:]: cr.line_to(x1, y1) pen = self.select_pen(highlight) cr.set_dash(pen.dash) cr.set_line_width(pen.linewidth) cr.set_source_rgba(*pen.color) cr.stroke() class BezierShape(Shape): def __init__(self, pen, points, filled=False): Shape.__init__(self) self.pen = pen.copy() self.points = points self.filled = filled def draw(self, cr, highlight=False): x0, y0 = self.points[0] cr.move_to(x0, y0) for i in xrange(1, len(self.points), 3): x1, y1 = self.points[i] x2, y2 = self.points[i + 1] x3, y3 = self.points[i + 2] cr.curve_to(x1, y1, x2, y2, x3, y3) pen = self.select_pen(highlight) if self.filled: cr.set_source_rgba(*pen.fillcolor) cr.fill_preserve() cr.fill() else: cr.set_dash(pen.dash) cr.set_line_width(pen.linewidth) cr.set_source_rgba(*pen.color) cr.stroke() class CompoundShape(Shape): def __init__(self, shapes): Shape.__init__(self) self.shapes = shapes def draw(self, cr, highlight=False): for shape in self.shapes: shape.draw(cr, highlight=highlight) class Url(object): def __init__(self, item, url, highlight=None): self.item = item self.url = url if highlight is None: highlight = set([item]) self.highlight = highlight class Jump(object): def __init__(self, item, x, y, highlight=None): self.item = item self.x = x self.y = y if highlight is None: highlight = set([item]) self.highlight = highlight class Element(CompoundShape): """Base class for graph nodes and edges.""" def __init__(self, shapes): CompoundShape.__init__(self, shapes) def get_url(self, x, y): return None def get_jump(self, x, y): return None class Node(Element): def __init__(self, x, y, w, h, shapes, url): Element.__init__(self, shapes) self.x = x self.y = y self.x1 = x - 0.5*w self.y1 = y - 0.5*h self.x2 = x + 0.5*w self.y2 = y + 0.5*h self.url = url def is_inside(self, x, y): return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 def get_url(self, x, y): if self.url is None: return None #print (x, y), (self.x1, self.y1), "-", (self.x2, self.y2) if self.is_inside(x, y): return Url(self, self.url) return None def get_jump(self, x, y): if self.is_inside(x, y): return Jump(self, self.x, self.y) return None def square_distance(x1, y1, x2, y2): deltax = x2 - x1 deltay = y2 - y1 return deltax*deltax + deltay*deltay class Edge(Element): def __init__(self, src, dst, points, shapes): Element.__init__(self, shapes) self.src = src self.dst = dst self.points = points RADIUS = 10 def get_jump(self, x, y): if square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS: return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst])) if square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS: return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src])) return None class Graph(Shape): def __init__(self, width=1, height=1, shapes=(), nodes=(), edges=()): Shape.__init__(self) self.width = width self.height = height self.shapes = shapes self.nodes = nodes self.edges = edges def get_size(self): return self.width, self.height def draw(self, cr, highlight_items=None): if highlight_items is None: highlight_items = () cr.set_source_rgba(0.0, 0.0, 0.0, 1.0) cr.set_line_cap(cairo.LINE_CAP_BUTT) cr.set_line_join(cairo.LINE_JOIN_MITER) for shape in self.shapes: shape.draw(cr) for edge in self.edges: edge.draw(cr, highlight=(edge in highlight_items)) for node in self.nodes: node.draw(cr, highlight=(node in highlight_items)) def get_url(self, x, y): for node in self.nodes: url = node.get_url(x, y) if url is not None: return url return None def get_jump(self, x, y): for edge in self.edges: jump = edge.get_jump(x, y) if jump is not None: return jump for node in self.nodes: jump = node.get_jump(x, y) if jump is not None: return jump return None class XDotAttrParser: """Parser for xdot drawing attributes. See also: - http://www.graphviz.org/doc/info/output.html#d:xdot """ def __init__(self, parser, buf): self.parser = parser self.buf = self.unescape(buf) self.pos = 0 self.pen = Pen() self.shapes = [] def __nonzero__(self): return self.pos < len(self.buf) def unescape(self, buf): buf = buf.replace('\\"', '"') buf = buf.replace('\\n', '\n') return buf def read_code(self): pos = self.buf.find(" ", self.pos) res = self.buf[self.pos:pos] self.pos = pos + 1 while self.pos < len(self.buf) and self.buf[self.pos].isspace(): self.pos += 1 return res def read_number(self): return int(self.read_code()) def read_float(self): return float(self.read_code()) def read_point(self): x = self.read_number() y = self.read_number() return self.transform(x, y) def read_text(self): num = self.read_number() pos = self.buf.find("-", self.pos) + 1 self.pos = pos + num res = self.buf[pos:self.pos] while self.pos < len(self.buf) and self.buf[self.pos].isspace(): self.pos += 1 return res def read_polygon(self): n = self.read_number() p = [] for i in range(n): x, y = self.read_point() p.append((x, y)) return p def read_color(self): # See http://www.graphviz.org/doc/info/attrs.html#k:color c = self.read_text() c1 = c[:1] if c1 == '#': hex2float = lambda h: float(int(h, 16)/255.0) r = hex2float(c[1:3]) g = hex2float(c[3:5]) b = hex2float(c[5:7]) try: a = hex2float(c[7:9]) except (IndexError, ValueError): a = 1.0 return r, g, b, a elif c1.isdigit() or c1 == ".": # "H,S,V" or "H S V" or "H, S, V" or any other variation h, s, v = map(float, c.replace(",", " ").split()) r, g, b = colorsys.hsv_to_rgb(h, s, v) a = 1.0 return r, g, b, a else: return self.lookup_color(c) def lookup_color(self, c): try: color = gtk.gdk.color_parse(c) except ValueError: pass else: s = 1.0/65535.0 r = color.red*s g = color.green*s b = color.blue*s a = 1.0 return r, g, b, a try: dummy, scheme, index = c.split('/') r, g, b = brewer_colors[scheme][int(index)] except (ValueError, KeyError): pass else: s = 1.0/255.0 r = r*s g = g*s b = b*s a = 1.0 return r, g, b, a sys.stderr.write("unknown color '%s'\n" % c) return None def parse(self): s = self while s: op = s.read_code() if op == "c": color = s.read_color() if color is not None: self.handle_color(color, filled=False) elif op == "C": color = s.read_color() if color is not None: self.handle_color(color, filled=True) elif op == "S": # http://www.graphviz.org/doc/info/attrs.html#k:style style = s.read_text() if style.startswith("setlinewidth("): lw = style.split("(")[1].split(")")[0] lw = float(lw) self.handle_linewidth(lw) elif style in ("solid", "dashed"): self.handle_linestyle(style) elif op == "F": size = s.read_float() name = s.read_text() self.handle_font(size, name) elif op == "T": x, y = s.read_point() j = s.read_number() w = s.read_number() t = s.read_text() self.handle_text(x, y, j, w, t) elif op == "E": x0, y0 = s.read_point() w = s.read_number() h = s.read_number() self.handle_ellipse(x0, y0, w, h, filled=True) elif op == "e": x0, y0 = s.read_point() w = s.read_number() h = s.read_number() self.handle_ellipse(x0, y0, w, h, filled=False) elif op == "L": points = self.read_polygon() self.handle_line(points) elif op == "B": points = self.read_polygon() self.handle_bezier(points, filled=False) elif op == "b": points = self.read_polygon() self.handle_bezier(points, filled=True) elif op == "P": points = self.read_polygon() self.handle_polygon(points, filled=True) elif op == "p": points = self.read_polygon() self.handle_polygon(points, filled=False) elif op == "I": x0, y0 = s.read_point() w = s.read_number() h = s.read_number() path = s.read_text() self.handle_image(x0, y0, w, h, path) else: sys.stderr.write("unknown xdot opcode '%s'\n" % op) break return self.shapes def transform(self, x, y): return self.parser.transform(x, y) def handle_color(self, color, filled=False): if filled: self.pen.fillcolor = color else: self.pen.color = color def handle_linewidth(self, linewidth): self.pen.linewidth = linewidth def handle_linestyle(self, style): if style == "solid": self.pen.dash = () elif style == "dashed": self.pen.dash = (6, ) # 6pt on, 6pt off def handle_font(self, size, name): self.pen.fontsize = size self.pen.fontname = name def handle_text(self, x, y, j, w, t): self.shapes.append(TextShape(self.pen, x, y, j, w, t)) def handle_ellipse(self, x0, y0, w, h, filled=False): if filled: # xdot uses this to mean "draw a filled shape with an outline" self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True)) self.shapes.append(EllipseShape(self.pen, x0, y0, w, h)) def handle_image(self, x0, y0, w, h, path): self.shapes.append(ImageShape(self.pen, x0, y0, w, h, path)) def handle_line(self, points): self.shapes.append(LineShape(self.pen, points)) def handle_bezier(self, points, filled=False): if filled: # xdot uses this to mean "draw a filled shape with an outline" self.shapes.append(BezierShape(self.pen, points, filled=True)) self.shapes.append(BezierShape(self.pen, points)) def handle_polygon(self, points, filled=False): if filled: # xdot uses this to mean "draw a filled shape with an outline" self.shapes.append(PolygonShape(self.pen, points, filled=True)) self.shapes.append(PolygonShape(self.pen, points)) EOF = -1 SKIP = -2 class ParseError(Exception): def __init__(self, msg=None, filename=None, line=None, col=None): self.msg = msg self.filename = filename self.line = line self.col = col def __str__(self): return ':'.join([str(part) for part in (self.filename, self.line, self.col, self.msg) if part != None]) class Scanner: """Stateless scanner.""" # should be overriden by derived classes tokens = [] symbols = {} literals = {} ignorecase = False def __init__(self): flags = re.DOTALL if self.ignorecase: flags |= re.IGNORECASE self.tokens_re = re.compile( '|'.join(['(' + regexp + ')' for type, regexp, test_lit in self.tokens]), flags ) def next(self, buf, pos): if pos >= len(buf): return EOF, '', pos mo = self.tokens_re.match(buf, pos) if mo: text = mo.group() type, regexp, test_lit = self.tokens[mo.lastindex - 1] pos = mo.end() if test_lit: type = self.literals.get(text, type) return type, text, pos else: c = buf[pos] return self.symbols.get(c, None), c, pos + 1 class Token: def __init__(self, type, text, line, col): self.type = type self.text = text self.line = line self.col = col class Lexer: # should be overriden by derived classes scanner = None tabsize = 8 newline_re = re.compile(r'\r\n?|\n') def __init__(self, buf = None, pos = 0, filename = None, fp = None): if fp is not None: try: fileno = fp.fileno() length = os.path.getsize(fp.name) import mmap except: # read whole file into memory buf = fp.read() pos = 0 else: # map the whole file into memory if length: # length must not be zero buf = mmap.mmap(fileno, length, access = mmap.ACCESS_READ) pos = os.lseek(fileno, 0, 1) else: buf = '' pos = 0 if filename is None: try: filename = fp.name except AttributeError: filename = None self.buf = buf self.pos = pos self.line = 1 self.col = 1 self.filename = filename def next(self): while True: # save state pos = self.pos line = self.line col = self.col type, text, endpos = self.scanner.next(self.buf, pos) assert pos + len(text) == endpos self.consume(text) type, text = self.filter(type, text) self.pos = endpos if type == SKIP: continue elif type is None: msg = 'unexpected char ' if text >= ' ' and text <= '~': msg += "'%s'" % text else: msg += "0x%X" % ord(text) raise ParseError(msg, self.filename, line, col) else: break return Token(type = type, text = text, line = line, col = col) def consume(self, text): # update line number pos = 0 for mo in self.newline_re.finditer(text, pos): self.line += 1 self.col = 1 pos = mo.end() # update column number while True: tabpos = text.find('\t', pos) if tabpos == -1: break self.col += tabpos - pos self.col = ((self.col - 1)//self.tabsize + 1)*self.tabsize + 1 pos = tabpos + 1 self.col += len(text) - pos class Parser: def __init__(self, lexer): self.lexer = lexer self.lookahead = self.lexer.next() def match(self, type): if self.lookahead.type != type: raise ParseError( msg = 'unexpected token %r' % self.lookahead.text, filename = self.lexer.filename, line = self.lookahead.line, col = self.lookahead.col) def skip(self, type): while self.lookahead.type != type: self.consume() def consume(self): token = self.lookahead self.lookahead = self.lexer.next() return token ID = 0 STR_ID = 1 HTML_ID = 2 EDGE_OP = 3 LSQUARE = 4 RSQUARE = 5 LCURLY = 6 RCURLY = 7 COMMA = 8 COLON = 9 SEMI = 10 EQUAL = 11 PLUS = 12 STRICT = 13 GRAPH = 14 DIGRAPH = 15 NODE = 16 EDGE = 17 SUBGRAPH = 18 class DotScanner(Scanner): # token regular expression table tokens = [ # whitespace and comments (SKIP, r'[ \t\f\r\n\v]+|' r'//[^\r\n]*|' r'/\*.*?\*/|' r'#[^\r\n]*', False), # Alphanumeric IDs (ID, r'[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*', True), # Numeric IDs (ID, r'-?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)', False), # String IDs (STR_ID, r'"[^"\\]*(?:\\.[^"\\]*)*"', False), # HTML IDs (HTML_ID, r'<[^<>]*(?:<[^<>]*>[^<>]*)*>', False), # Edge operators (EDGE_OP, r'-[>-]', False), ] # symbol table symbols = { '[': LSQUARE, ']': RSQUARE, '{': LCURLY, '}': RCURLY, ',': COMMA, ':': COLON, ';': SEMI, '=': EQUAL, '+': PLUS, } # literal table literals = { 'strict': STRICT, 'graph': GRAPH, 'digraph': DIGRAPH, 'node': NODE, 'edge': EDGE, 'subgraph': SUBGRAPH, } ignorecase = True class DotLexer(Lexer): scanner = DotScanner() def filter(self, type, text): # TODO: handle charset if type == STR_ID: text = text[1:-1] # line continuations text = text.replace('\\\r\n', '') text = text.replace('\\\r', '') text = text.replace('\\\n', '') text = text.replace('\\r', '\r') text = text.replace('\\n', '\n') text = text.replace('\\t', '\t') text = text.replace('\\', '') type = ID elif type == HTML_ID: text = text[1:-1] type = ID return type, text class DotParser(Parser): def __init__(self, lexer): Parser.__init__(self, lexer) self.graph_attrs = {} self.node_attrs = {} self.edge_attrs = {} def parse(self): self.parse_graph() self.match(EOF) def parse_graph(self): if self.lookahead.type == STRICT: self.consume() self.skip(LCURLY) self.consume() while self.lookahead.type != RCURLY: self.parse_stmt() self.consume() def parse_subgraph(self): id = None if self.lookahead.type == SUBGRAPH: self.consume() if self.lookahead.type == ID: id = self.lookahead.text self.consume() if self.lookahead.type == LCURLY: self.consume() while self.lookahead.type != RCURLY: self.parse_stmt() self.consume() return id def parse_stmt(self): if self.lookahead.type == GRAPH: self.consume() attrs = self.parse_attrs() self.graph_attrs.update(attrs) self.handle_graph(attrs) elif self.lookahead.type == NODE: self.consume() self.node_attrs.update(self.parse_attrs()) elif self.lookahead.type == EDGE: self.consume() self.edge_attrs.update(self.parse_attrs()) elif self.lookahead.type in (SUBGRAPH, LCURLY): self.parse_subgraph() else: id = self.parse_node_id() if self.lookahead.type == EDGE_OP: self.consume() node_ids = [id, self.parse_node_id()] while self.lookahead.type == EDGE_OP: node_ids.append(self.parse_node_id()) attrs = self.parse_attrs() for i in range(0, len(node_ids) - 1): self.handle_edge(node_ids[i], node_ids[i + 1], attrs) elif self.lookahead.type == EQUAL: self.consume() self.parse_id() else: attrs = self.parse_attrs() self.handle_node(id, attrs) if self.lookahead.type == SEMI: self.consume() def parse_attrs(self): attrs = {} while self.lookahead.type == LSQUARE: self.consume() while self.lookahead.type != RSQUARE: name, value = self.parse_attr() attrs[name] = value if self.lookahead.type == COMMA: self.consume() self.consume() return attrs def parse_attr(self): name = self.parse_id() if self.lookahead.type == EQUAL: self.consume() value = self.parse_id() else: value = 'true' return name, value def parse_node_id(self): node_id = self.parse_id() if self.lookahead.type == COLON: self.consume() port = self.parse_id() if self.lookahead.type == COLON: self.consume() compass_pt = self.parse_id() else: compass_pt = None else: port = None compass_pt = None # XXX: we don't really care about port and compass point values when parsing xdot return node_id def parse_id(self): self.match(ID) id = self.lookahead.text self.consume() return id def handle_graph(self, attrs): pass def handle_node(self, id, attrs): pass def handle_edge(self, src_id, dst_id, attrs): pass class XDotParser(DotParser): def __init__(self, xdotcode): lexer = DotLexer(buf = xdotcode) DotParser.__init__(self, lexer) self.nodes = [] self.edges = [] self.shapes = [] self.node_by_name = {} self.top_graph = True def handle_graph(self, attrs): if self.top_graph: try: bb = attrs['bb'] except KeyError: return if not bb: return xmin, ymin, xmax, ymax = map(float, bb.split(",")) self.xoffset = -xmin self.yoffset = -ymax self.xscale = 1.0 self.yscale = -1.0 # FIXME: scale from points to pixels self.width = xmax - xmin self.height = ymax - ymin self.top_graph = False for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"): if attr in attrs: parser = XDotAttrParser(self, attrs[attr]) self.shapes.extend(parser.parse()) def handle_node(self, id, attrs): try: pos = attrs['pos'] except KeyError: return x, y = self.parse_node_pos(pos) w = float(attrs['width'])*72 h = float(attrs['height'])*72 shapes = [] for attr in ("_draw_", "_ldraw_"): if attr in attrs: parser = XDotAttrParser(self, attrs[attr]) shapes.extend(parser.parse()) url = attrs.get('URL', None) node = Node(x, y, w, h, shapes, url) self.node_by_name[id] = node if shapes: self.nodes.append(node) def handle_edge(self, src_id, dst_id, attrs): try: pos = attrs['pos'] except KeyError: return points = self.parse_edge_pos(pos) shapes = [] for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"): if attr in attrs: parser = XDotAttrParser(self, attrs[attr]) shapes.extend(parser.parse()) if shapes: src = self.node_by_name[src_id] dst = self.node_by_name[dst_id] self.edges.append(Edge(src, dst, points, shapes)) def parse(self): DotParser.parse(self) return Graph(self.width, self.height, self.shapes, self.nodes, self.edges) def parse_node_pos(self, pos): x, y = pos.split(",") return self.transform(float(x), float(y)) def parse_edge_pos(self, pos): points = [] for entry in pos.split(' '): fields = entry.split(',') try: x, y = fields except ValueError: # TODO: handle start/end points continue else: points.append(self.transform(float(x), float(y))) return points def transform(self, x, y): # XXX: this is not the right place for this code x = (x + self.xoffset)*self.xscale y = (y + self.yoffset)*self.yscale return x, y class Animation(object): step = 0.03 # seconds def __init__(self, dot_widget): self.dot_widget = dot_widget self.timeout_id = None def start(self): self.timeout_id = gobject.timeout_add(int(self.step * 1000), self.tick) def stop(self): self.dot_widget.animation = NoAnimation(self.dot_widget) if self.timeout_id is not None: gobject.source_remove(self.timeout_id) self.timeout_id = None def tick(self): self.stop() class NoAnimation(Animation): def start(self): pass def stop(self): pass class LinearAnimation(Animation): duration = 0.6 def start(self): self.started = time.time() Animation.start(self) def tick(self): t = (time.time() - self.started) / self.duration self.animate(max(0, min(t, 1))) return (t < 1) def animate(self, t): pass class MoveToAnimation(LinearAnimation): def __init__(self, dot_widget, target_x, target_y): Animation.__init__(self, dot_widget) self.source_x = dot_widget.x self.source_y = dot_widget.y self.target_x = target_x self.target_y = target_y def animate(self, t): sx, sy = self.source_x, self.source_y tx, ty = self.target_x, self.target_y self.dot_widget.x = tx * t + sx * (1-t) self.dot_widget.y = ty * t + sy * (1-t) self.dot_widget.queue_draw() class ZoomToAnimation(MoveToAnimation): def __init__(self, dot_widget, target_x, target_y): MoveToAnimation.__init__(self, dot_widget, target_x, target_y) self.source_zoom = dot_widget.zoom_ratio self.target_zoom = self.source_zoom self.extra_zoom = 0 middle_zoom = 0.5 * (self.source_zoom + self.target_zoom) distance = math.hypot(self.source_x - self.target_x, self.source_y - self.target_y) rect = self.dot_widget.get_allocation() visible = min(rect.width, rect.height) / self.dot_widget.zoom_ratio visible *= 0.9 if distance > 0: desired_middle_zoom = visible / distance self.extra_zoom = min(0, 4 * (desired_middle_zoom - middle_zoom)) def animate(self, t): a, b, c = self.source_zoom, self.extra_zoom, self.target_zoom self.dot_widget.zoom_ratio = c*t + b*t*(1-t) + a*(1-t) self.dot_widget.zoom_to_fit_on_resize = False MoveToAnimation.animate(self, t) class DragAction(object): def __init__(self, dot_widget): self.dot_widget = dot_widget def on_button_press(self, event): self.startmousex = self.prevmousex = event.x self.startmousey = self.prevmousey = event.y self.start() def on_motion_notify(self, event): if event.is_hint: x, y, state = event.window.get_pointer() else: x, y, state = event.x, event.y, event.state deltax = self.prevmousex - x deltay = self.prevmousey - y self.drag(deltax, deltay) self.prevmousex = x self.prevmousey = y def on_button_release(self, event): self.stopmousex = event.x self.stopmousey = event.y self.stop() def draw(self, cr): pass def start(self): pass def drag(self, deltax, deltay): pass def stop(self): pass def abort(self): pass class NullAction(DragAction): def on_motion_notify(self, event): if event.is_hint: x, y, state = event.window.get_pointer() else: x, y, state = event.x, event.y, event.state dot_widget = self.dot_widget item = dot_widget.get_url(x, y) if item is None: item = dot_widget.get_jump(x, y) if item is not None: dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) dot_widget.set_highlight(item.highlight) else: dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW)) dot_widget.set_highlight(None) class PanAction(DragAction): def start(self): self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) def drag(self, deltax, deltay): self.dot_widget.x += deltax / self.dot_widget.zoom_ratio self.dot_widget.y += deltay / self.dot_widget.zoom_ratio self.dot_widget.queue_draw() def stop(self): self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW)) abort = stop class ZoomAction(DragAction): def drag(self, deltax, deltay): self.dot_widget.zoom_ratio *= 1.005 ** (deltax + deltay) self.dot_widget.zoom_to_fit_on_resize = False self.dot_widget.queue_draw() def stop(self): self.dot_widget.queue_draw() class ZoomAreaAction(DragAction): def drag(self, deltax, deltay): self.dot_widget.queue_draw() def draw(self, cr): cr.save() cr.set_source_rgba(.5, .5, 1.0, 0.25) cr.rectangle(self.startmousex, self.startmousey, self.prevmousex - self.startmousex, self.prevmousey - self.startmousey) cr.fill() cr.set_source_rgba(.5, .5, 1.0, 1.0) cr.set_line_width(1) cr.rectangle(self.startmousex - .5, self.startmousey - .5, self.prevmousex - self.startmousex + 1, self.prevmousey - self.startmousey + 1) cr.stroke() cr.restore() def stop(self): x1, y1 = self.dot_widget.window2graph(self.startmousex, self.startmousey) x2, y2 = self.dot_widget.window2graph(self.stopmousex, self.stopmousey) self.dot_widget.zoom_to_area(x1, y1, x2, y2) def abort(self): self.dot_widget.queue_draw() class DotWidget(gtk.DrawingArea): """PyGTK widget that draws dot graphs.""" __gsignals__ = { 'expose-event': 'override', 'clicked' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gtk.gdk.Event)) } filter = 'dot' def __init__(self): gtk.DrawingArea.__init__(self) self.graph = Graph() self.openfilename = None self.set_flags(gtk.CAN_FOCUS) self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) self.connect("button-press-event", self.on_area_button_press) self.connect("button-release-event", self.on_area_button_release) self.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_RELEASE_MASK) self.connect("motion-notify-event", self.on_area_motion_notify) self.connect("scroll-event", self.on_area_scroll_event) self.connect("size-allocate", self.on_area_size_allocate) self.connect('key-press-event', self.on_key_press_event) self.x, self.y = 0.0, 0.0 self.zoom_ratio = 1.0 self.zoom_to_fit_on_resize = False self.animation = NoAnimation(self) self.drag_action = NullAction(self) self.presstime = None self.highlight = None def set_filter(self, filter): self.filter = filter def set_dotcode(self, dotcode, filename=''): if isinstance(dotcode, unicode): dotcode = dotcode.encode('utf8') p = subprocess.Popen( [self.filter, '-Txdot'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True ) xdotcode, error = p.communicate(dotcode) if p.returncode != 0: dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, message_format=error, buttons=gtk.BUTTONS_OK) dialog.set_title('Dot Viewer') dialog.run() dialog.destroy() return False try: self.set_xdotcode(xdotcode) except ParseError, ex: dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, message_format=str(ex), buttons=gtk.BUTTONS_OK) dialog.set_title('Dot Viewer') dialog.run() dialog.destroy() return False else: self.openfilename = filename return True def set_xdotcode(self, xdotcode): #print xdotcode parser = XDotParser(xdotcode) self.graph = parser.parse() self.zoom_image(self.zoom_ratio, center=True) def reload(self): if self.openfilename is not None: try: fp = file(self.openfilename, 'rt') self.set_dotcode(fp.read(), self.openfilename) fp.close() except IOError: pass def do_expose_event(self, event): cr = self.window.cairo_create() # set a clip region for the expose event cr.rectangle( event.area.x, event.area.y, event.area.width, event.area.height ) cr.clip() cr.set_source_rgba(1.0, 1.0, 1.0, 1.0) cr.paint() cr.save() rect = self.get_allocation() cr.translate(0.5*rect.width, 0.5*rect.height) cr.scale(self.zoom_ratio, self.zoom_ratio) cr.translate(-self.x, -self.y) self.graph.draw(cr, highlight_items=self.highlight) cr.restore() self.drag_action.draw(cr) return False def get_current_pos(self): return self.x, self.y def set_current_pos(self, x, y): self.x = x self.y = y self.queue_draw() def set_highlight(self, items): if self.highlight != items: self.highlight = items self.queue_draw() def zoom_image(self, zoom_ratio, center=False, pos=None): if center: self.x = self.graph.width/2 self.y = self.graph.height/2 elif pos is not None: rect = self.get_allocation() x, y = pos x -= 0.5*rect.width y -= 0.5*rect.height self.x += x / self.zoom_ratio - x / zoom_ratio self.y += y / self.zoom_ratio - y / zoom_ratio self.zoom_ratio = zoom_ratio self.zoom_to_fit_on_resize = False self.queue_draw() def zoom_to_area(self, x1, y1, x2, y2): rect = self.get_allocation() width = abs(x1 - x2) height = abs(y1 - y2) self.zoom_ratio = min( float(rect.width)/float(width), float(rect.height)/float(height) ) self.zoom_to_fit_on_resize = False self.x = (x1 + x2) / 2 self.y = (y1 + y2) / 2 self.queue_draw() def zoom_to_fit(self): rect = self.get_allocation() rect.x += self.ZOOM_TO_FIT_MARGIN rect.y += self.ZOOM_TO_FIT_MARGIN rect.width -= 2 * self.ZOOM_TO_FIT_MARGIN rect.height -= 2 * self.ZOOM_TO_FIT_MARGIN zoom_ratio = min( float(rect.width)/float(self.graph.width), float(rect.height)/float(self.graph.height) ) self.zoom_image(zoom_ratio, center=True) self.zoom_to_fit_on_resize = True ZOOM_INCREMENT = 1.25 ZOOM_TO_FIT_MARGIN = 12 def on_zoom_in(self, action): self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT) def on_zoom_out(self, action): self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT) def on_zoom_fit(self, action): self.zoom_to_fit() def on_zoom_100(self, action): self.zoom_image(1.0) POS_INCREMENT = 100 def on_key_press_event(self, widget, event): if event.keyval == gtk.keysyms.Left: self.x -= self.POS_INCREMENT/self.zoom_ratio self.queue_draw() return True if event.keyval == gtk.keysyms.Right: self.x += self.POS_INCREMENT/self.zoom_ratio self.queue_draw() return True if event.keyval == gtk.keysyms.Up: self.y -= self.POS_INCREMENT/self.zoom_ratio self.queue_draw() return True if event.keyval == gtk.keysyms.Down: self.y += self.POS_INCREMENT/self.zoom_ratio self.queue_draw() return True if event.keyval == gtk.keysyms.Page_Up: self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT) self.queue_draw() return True if event.keyval == gtk.keysyms.Page_Down: self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT) self.queue_draw() return True if event.keyval == gtk.keysyms.Escape: self.drag_action.abort() self.drag_action = NullAction(self) return True if event.keyval == gtk.keysyms.r: self.reload() return True if event.keyval == gtk.keysyms.q: gtk.main_quit() return True return False def get_drag_action(self, event): state = event.state if event.button in (1, 2): # left or middle button if state & gtk.gdk.CONTROL_MASK: return ZoomAction elif state & gtk.gdk.SHIFT_MASK: return ZoomAreaAction else: return PanAction return NullAction def on_area_button_press(self, area, event): self.animation.stop() self.drag_action.abort() action_type = self.get_drag_action(event) self.drag_action = action_type(self) self.drag_action.on_button_press(event) self.presstime = time.time() self.pressx = event.x self.pressy = event.y return False def is_click(self, event, click_fuzz=4, click_timeout=1.0): assert event.type == gtk.gdk.BUTTON_RELEASE if self.presstime is None: # got a button release without seeing the press? return False # XXX instead of doing this complicated logic, shouldn't we listen # for gtk's clicked event instead? deltax = self.pressx - event.x deltay = self.pressy - event.y return (time.time() < self.presstime + click_timeout and math.hypot(deltax, deltay) < click_fuzz) def on_area_button_release(self, area, event): self.drag_action.on_button_release(event) self.drag_action = NullAction(self) if event.button == 1 and self.is_click(event): x, y = int(event.x), int(event.y) url = self.get_url(x, y) if url is not None: self.emit('clicked', unicode(url.url), event) else: jump = self.get_jump(x, y) if jump is not None: self.animate_to(jump.x, jump.y) return True if event.button == 1 or event.button == 2: return True return False def on_area_scroll_event(self, area, event): if event.direction == gtk.gdk.SCROLL_UP: self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT, pos=(event.x, event.y)) return True if event.direction == gtk.gdk.SCROLL_DOWN: self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT, pos=(event.x, event.y)) return True return False def on_area_motion_notify(self, area, event): self.drag_action.on_motion_notify(event) return True def on_area_size_allocate(self, area, allocation): if self.zoom_to_fit_on_resize: self.zoom_to_fit() def animate_to(self, x, y): self.animation = ZoomToAnimation(self, x, y) self.animation.start() def window2graph(self, x, y): rect = self.get_allocation() x -= 0.5*rect.width y -= 0.5*rect.height x /= self.zoom_ratio y /= self.zoom_ratio x += self.x y += self.y return x, y def get_url(self, x, y): x, y = self.window2graph(x, y) return self.graph.get_url(x, y) def get_jump(self, x, y): x, y = self.window2graph(x, y) return self.graph.get_jump(x, y) class DotWindow(gtk.Window): ui = ''' ''' def __init__(self, window): #gtk.Window.__init__(self) self.graph = Graph() #window = self #window.set_title('Dot Viewer') #window.set_default_size(512, 512) vbox = gtk.VBox() window.add(vbox) self.widget = DotWidget() # Create a UIManager instance uimanager = self.uimanager = gtk.UIManager() # Add the accelerator group to the toplevel window #accelgroup = uimanager.get_accel_group() #window.add_accel_group(accelgroup) # Create an ActionGroup actiongroup = gtk.ActionGroup('Actions') self.actiongroup = actiongroup # Create actions actiongroup.add_actions(( # ('Open', gtk.STOCK_OPEN, None, None, None, self.on_open), ('ZoomIn', gtk.STOCK_ZOOM_IN, None, None, None, self.widget.on_zoom_in), ('ZoomOut', gtk.STOCK_ZOOM_OUT, None, None, None, self.widget.on_zoom_out), ('ZoomFit', gtk.STOCK_ZOOM_FIT, None, None, None, self.widget.on_zoom_fit), ('Zoom100', gtk.STOCK_ZOOM_100, None, None, None, self.widget.on_zoom_100), )) # Add the actiongroup to the uimanager uimanager.insert_action_group(actiongroup, 0) # Add a UI descrption uimanager.add_ui_from_string(self.ui) # Create a Toolbar toolbar = uimanager.get_widget('/ToolBar') toolbar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#d5e5f7")) vbox.pack_start(toolbar, False) vbox.pack_start(self.widget) #self.set_focus(self.widget) #self.show_all() self.widget.zoom_image(1.0) def update(self, filename): import os if not hasattr(self, "last_mtime"): self.last_mtime = None current_mtime = os.stat(filename).st_mtime if current_mtime != self.last_mtime: self.last_mtime = current_mtime self.open_file(filename) return True def set_filter(self, filter): self.widget.set_filter(filter) def set_dotcode(self, dotcode, filename=''): if self.widget.set_dotcode(dotcode, filename): #self.set_title(os.path.basename(filename) + ' - Dot Viewer') #self.widget.zoom_to_fit() self.widget.zoom_image(1.0) def set_xdotcode(self, xdotcode, filename=''): if self.widget.set_xdotcode(xdotcode): #self.set_title(os.path.basename(filename) + ' - Dot Viewer') self.widget.zoom_to_fit() def open_file(self, filename): try: fp = file(filename, 'rt') self.set_dotcode(fp.read(), filename) fp.close() except IOError, ex: dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, message_format=str(ex), buttons=gtk.BUTTONS_OK) dlg.set_title('Dot Viewer') dlg.run() dlg.destroy() def on_open(self, action): chooser = gtk.FileChooserDialog(title="Open dot File", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.set_name("Graphviz dot files") filter.add_pattern("*.dot") chooser.add_filter(filter) filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") chooser.add_filter(filter) if chooser.run() == gtk.RESPONSE_OK: filename = chooser.get_filename() chooser.destroy() self.open_file(filename) else: chooser.destroy() def on_reload(self, action): self.widget.reload() def main(): import optparse parser = optparse.OptionParser( usage='\n\t%prog [file]', version='%%prog %s' % __version__) parser.add_option( '-f', '--filter', type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'), dest='filter', default='dot', help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]') (options, args) = parser.parse_args(sys.argv[1:]) if len(args) > 1: parser.error('incorrect number of arguments') win = DotWindow() win.connect('destroy', gtk.main_quit) win.set_filter(options.filter) if len(args) >= 1: if args[0] == '-': win.set_dotcode(sys.stdin.read()) else: win.open_file(args[0]) gobject.timeout_add(1000, win.update, args[0]) gtk.main() # Apache-Style Software License for ColorBrewer software and ColorBrewer Color # Schemes, Version 1.1 # # Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State # University. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions as source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. The end-user documentation included with the redistribution, if any, # must include the following acknowledgment: # # This product includes color specifications and designs developed by # Cynthia Brewer (http://colorbrewer.org/). # # Alternately, this acknowledgment may appear in the software itself, if and # wherever such third-party acknowledgments normally appear. # # 3. The name "ColorBrewer" must not be used to endorse or promote products # derived from this software without prior written permission. For written # permission, please contact Cynthia Brewer at cbrewer@psu.edu. # # 4. Products derived from this software may not be called "ColorBrewer", # nor may "ColorBrewer" appear in their name, without prior written # permission of Cynthia Brewer. # # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA # BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. brewer_colors = { 'accent3': [(127, 201, 127), (190, 174, 212), (253, 192, 134)], 'accent4': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153)], 'accent5': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176)], 'accent6': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127)], 'accent7': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23)], 'accent8': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23), (102, 102, 102)], 'blues3': [(222, 235, 247), (158, 202, 225), (49, 130, 189)], 'blues4': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (33, 113, 181)], 'blues5': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (49, 130, 189), (8, 81, 156)], 'blues6': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (49, 130, 189), (8, 81, 156)], 'blues7': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)], 'blues8': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)], 'blues9': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 81, 156), (8, 48, 107)], 'brbg10': [(84, 48, 5), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], 'brbg11': [(84, 48, 5), (1, 102, 94), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143)], 'brbg3': [(216, 179, 101), (245, 245, 245), (90, 180, 172)], 'brbg4': [(166, 97, 26), (223, 194, 125), (128, 205, 193), (1, 133, 113)], 'brbg5': [(166, 97, 26), (223, 194, 125), (245, 245, 245), (128, 205, 193), (1, 133, 113)], 'brbg6': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (199, 234, 229), (90, 180, 172), (1, 102, 94)], 'brbg7': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (245, 245, 245), (199, 234, 229), (90, 180, 172), (1, 102, 94)], 'brbg8': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], 'brbg9': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], 'bugn3': [(229, 245, 249), (153, 216, 201), (44, 162, 95)], 'bugn4': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (35, 139, 69)], 'bugn5': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (44, 162, 95), (0, 109, 44)], 'bugn6': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (44, 162, 95), (0, 109, 44)], 'bugn7': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)], 'bugn8': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)], 'bugn9': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 109, 44), (0, 68, 27)], 'bupu3': [(224, 236, 244), (158, 188, 218), (136, 86, 167)], 'bupu4': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 65, 157)], 'bupu5': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 86, 167), (129, 15, 124)], 'bupu6': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (136, 86, 167), (129, 15, 124)], 'bupu7': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)], 'bupu8': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)], 'bupu9': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (129, 15, 124), (77, 0, 75)], 'dark23': [(27, 158, 119), (217, 95, 2), (117, 112, 179)], 'dark24': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138)], 'dark25': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30)], 'dark26': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2)], 'dark27': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29)], 'dark28': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29), (102, 102, 102)], 'gnbu3': [(224, 243, 219), (168, 221, 181), (67, 162, 202)], 'gnbu4': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (43, 140, 190)], 'gnbu5': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (67, 162, 202), (8, 104, 172)], 'gnbu6': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (67, 162, 202), (8, 104, 172)], 'gnbu7': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)], 'gnbu8': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)], 'gnbu9': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 104, 172), (8, 64, 129)], 'greens3': [(229, 245, 224), (161, 217, 155), (49, 163, 84)], 'greens4': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (35, 139, 69)], 'greens5': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (49, 163, 84), (0, 109, 44)], 'greens6': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (49, 163, 84), (0, 109, 44)], 'greens7': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)], 'greens8': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)], 'greens9': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 109, 44), (0, 68, 27)], 'greys3': [(240, 240, 240), (189, 189, 189), (99, 99, 99)], 'greys4': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (82, 82, 82)], 'greys5': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (99, 99, 99), (37, 37, 37)], 'greys6': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (99, 99, 99), (37, 37, 37)], 'greys7': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)], 'greys8': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)], 'greys9': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37), (0, 0, 0)], 'oranges3': [(254, 230, 206), (253, 174, 107), (230, 85, 13)], 'oranges4': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (217, 71, 1)], 'oranges5': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (230, 85, 13), (166, 54, 3)], 'oranges6': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (230, 85, 13), (166, 54, 3)], 'oranges7': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)], 'oranges8': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)], 'oranges9': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (166, 54, 3), (127, 39, 4)], 'orrd3': [(254, 232, 200), (253, 187, 132), (227, 74, 51)], 'orrd4': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (215, 48, 31)], 'orrd5': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (227, 74, 51), (179, 0, 0)], 'orrd6': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (227, 74, 51), (179, 0, 0)], 'orrd7': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)], 'orrd8': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)], 'orrd9': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (179, 0, 0), (127, 0, 0)], 'paired10': [(166, 206, 227), (106, 61, 154), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], 'paired11': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], 'paired12': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (177, 89, 40), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], 'paired3': [(166, 206, 227), (31, 120, 180), (178, 223, 138)], 'paired4': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44)], 'paired5': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153)], 'paired6': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28)], 'paired7': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111)], 'paired8': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0)], 'paired9': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], 'pastel13': [(251, 180, 174), (179, 205, 227), (204, 235, 197)], 'pastel14': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228)], 'pastel15': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166)], 'pastel16': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204)], 'pastel17': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189)], 'pastel18': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236)], 'pastel19': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236), (242, 242, 242)], 'pastel23': [(179, 226, 205), (253, 205, 172), (203, 213, 232)], 'pastel24': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228)], 'pastel25': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201)], 'pastel26': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174)], 'pastel27': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204)], 'pastel28': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204), (204, 204, 204)], 'piyg10': [(142, 1, 82), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], 'piyg11': [(142, 1, 82), (77, 146, 33), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65)], 'piyg3': [(233, 163, 201), (247, 247, 247), (161, 215, 106)], 'piyg4': [(208, 28, 139), (241, 182, 218), (184, 225, 134), (77, 172, 38)], 'piyg5': [(208, 28, 139), (241, 182, 218), (247, 247, 247), (184, 225, 134), (77, 172, 38)], 'piyg6': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (230, 245, 208), (161, 215, 106), (77, 146, 33)], 'piyg7': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (247, 247, 247), (230, 245, 208), (161, 215, 106), (77, 146, 33)], 'piyg8': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], 'piyg9': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], 'prgn10': [(64, 0, 75), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], 'prgn11': [(64, 0, 75), (27, 120, 55), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97)], 'prgn3': [(175, 141, 195), (247, 247, 247), (127, 191, 123)], 'prgn4': [(123, 50, 148), (194, 165, 207), (166, 219, 160), (0, 136, 55)], 'prgn5': [(123, 50, 148), (194, 165, 207), (247, 247, 247), (166, 219, 160), (0, 136, 55)], 'prgn6': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (217, 240, 211), (127, 191, 123), (27, 120, 55)], 'prgn7': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (247, 247, 247), (217, 240, 211), (127, 191, 123), (27, 120, 55)], 'prgn8': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], 'prgn9': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], 'pubu3': [(236, 231, 242), (166, 189, 219), (43, 140, 190)], 'pubu4': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (5, 112, 176)], 'pubu5': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (43, 140, 190), (4, 90, 141)], 'pubu6': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (43, 140, 190), (4, 90, 141)], 'pubu7': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)], 'pubu8': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)], 'pubu9': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (4, 90, 141), (2, 56, 88)], 'pubugn3': [(236, 226, 240), (166, 189, 219), (28, 144, 153)], 'pubugn4': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (2, 129, 138)], 'pubugn5': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (28, 144, 153), (1, 108, 89)], 'pubugn6': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (28, 144, 153), (1, 108, 89)], 'pubugn7': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)], 'pubugn8': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)], 'pubugn9': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 108, 89), (1, 70, 54)], 'puor10': [(127, 59, 8), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], 'puor11': [(127, 59, 8), (84, 39, 136), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172)], 'puor3': [(241, 163, 64), (247, 247, 247), (153, 142, 195)], 'puor4': [(230, 97, 1), (253, 184, 99), (178, 171, 210), (94, 60, 153)], 'puor5': [(230, 97, 1), (253, 184, 99), (247, 247, 247), (178, 171, 210), (94, 60, 153)], 'puor6': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (216, 218, 235), (153, 142, 195), (84, 39, 136)], 'puor7': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (247, 247, 247), (216, 218, 235), (153, 142, 195), (84, 39, 136)], 'puor8': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], 'puor9': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], 'purd3': [(231, 225, 239), (201, 148, 199), (221, 28, 119)], 'purd4': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (206, 18, 86)], 'purd5': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (221, 28, 119), (152, 0, 67)], 'purd6': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (221, 28, 119), (152, 0, 67)], 'purd7': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)], 'purd8': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)], 'purd9': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (152, 0, 67), (103, 0, 31)], 'purples3': [(239, 237, 245), (188, 189, 220), (117, 107, 177)], 'purples4': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (106, 81, 163)], 'purples5': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (117, 107, 177), (84, 39, 143)], 'purples6': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (117, 107, 177), (84, 39, 143)], 'purples7': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)], 'purples8': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)], 'purples9': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (84, 39, 143), (63, 0, 125)], 'rdbu10': [(103, 0, 31), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], 'rdbu11': [(103, 0, 31), (33, 102, 172), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195)], 'rdbu3': [(239, 138, 98), (247, 247, 247), (103, 169, 207)], 'rdbu4': [(202, 0, 32), (244, 165, 130), (146, 197, 222), (5, 113, 176)], 'rdbu5': [(202, 0, 32), (244, 165, 130), (247, 247, 247), (146, 197, 222), (5, 113, 176)], 'rdbu6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (209, 229, 240), (103, 169, 207), (33, 102, 172)], 'rdbu7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (247, 247, 247), (209, 229, 240), (103, 169, 207), (33, 102, 172)], 'rdbu8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], 'rdbu9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], 'rdgy10': [(103, 0, 31), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], 'rdgy11': [(103, 0, 31), (77, 77, 77), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135)], 'rdgy3': [(239, 138, 98), (255, 255, 255), (153, 153, 153)], 'rdgy4': [(202, 0, 32), (244, 165, 130), (186, 186, 186), (64, 64, 64)], 'rdgy5': [(202, 0, 32), (244, 165, 130), (255, 255, 255), (186, 186, 186), (64, 64, 64)], 'rdgy6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (224, 224, 224), (153, 153, 153), (77, 77, 77)], 'rdgy7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (255, 255, 255), (224, 224, 224), (153, 153, 153), (77, 77, 77)], 'rdgy8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], 'rdgy9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], 'rdpu3': [(253, 224, 221), (250, 159, 181), (197, 27, 138)], 'rdpu4': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (174, 1, 126)], 'rdpu5': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (197, 27, 138), (122, 1, 119)], 'rdpu6': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (197, 27, 138), (122, 1, 119)], 'rdpu7': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)], 'rdpu8': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)], 'rdpu9': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119), (73, 0, 106)], 'rdylbu10': [(165, 0, 38), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], 'rdylbu11': [(165, 0, 38), (69, 117, 180), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209)], 'rdylbu3': [(252, 141, 89), (255, 255, 191), (145, 191, 219)], 'rdylbu4': [(215, 25, 28), (253, 174, 97), (171, 217, 233), (44, 123, 182)], 'rdylbu5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 217, 233), (44, 123, 182)], 'rdylbu6': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (224, 243, 248), (145, 191, 219), (69, 117, 180)], 'rdylbu7': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (255, 255, 191), (224, 243, 248), (145, 191, 219), (69, 117, 180)], 'rdylbu8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], 'rdylbu9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], 'rdylgn10': [(165, 0, 38), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], 'rdylgn11': [(165, 0, 38), (26, 152, 80), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99)], 'rdylgn3': [(252, 141, 89), (255, 255, 191), (145, 207, 96)], 'rdylgn4': [(215, 25, 28), (253, 174, 97), (166, 217, 106), (26, 150, 65)], 'rdylgn5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (166, 217, 106), (26, 150, 65)], 'rdylgn6': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (217, 239, 139), (145, 207, 96), (26, 152, 80)], 'rdylgn7': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (255, 255, 191), (217, 239, 139), (145, 207, 96), (26, 152, 80)], 'rdylgn8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], 'rdylgn9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], 'reds3': [(254, 224, 210), (252, 146, 114), (222, 45, 38)], 'reds4': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (203, 24, 29)], 'reds5': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (222, 45, 38), (165, 15, 21)], 'reds6': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (222, 45, 38), (165, 15, 21)], 'reds7': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)], 'reds8': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)], 'reds9': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (165, 15, 21), (103, 0, 13)], 'set13': [(228, 26, 28), (55, 126, 184), (77, 175, 74)], 'set14': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163)], 'set15': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0)], 'set16': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51)], 'set17': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40)], 'set18': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191)], 'set19': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191), (153, 153, 153)], 'set23': [(102, 194, 165), (252, 141, 98), (141, 160, 203)], 'set24': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195)], 'set25': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84)], 'set26': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47)], 'set27': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148)], 'set28': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148), (179, 179, 179)], 'set310': [(141, 211, 199), (188, 128, 189), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], 'set311': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], 'set312': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 237, 111), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], 'set33': [(141, 211, 199), (255, 255, 179), (190, 186, 218)], 'set34': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114)], 'set35': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211)], 'set36': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98)], 'set37': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105)], 'set38': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229)], 'set39': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], 'spectral10': [(158, 1, 66), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], 'spectral11': [(158, 1, 66), (50, 136, 189), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165)], 'spectral3': [(252, 141, 89), (255, 255, 191), (153, 213, 148)], 'spectral4': [(215, 25, 28), (253, 174, 97), (171, 221, 164), (43, 131, 186)], 'spectral5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 221, 164), (43, 131, 186)], 'spectral6': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (230, 245, 152), (153, 213, 148), (50, 136, 189)], 'spectral7': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (255, 255, 191), (230, 245, 152), (153, 213, 148), (50, 136, 189)], 'spectral8': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], 'spectral9': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], 'ylgn3': [(247, 252, 185), (173, 221, 142), (49, 163, 84)], 'ylgn4': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (35, 132, 67)], 'ylgn5': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (49, 163, 84), (0, 104, 55)], 'ylgn6': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (49, 163, 84), (0, 104, 55)], 'ylgn7': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)], 'ylgn8': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)], 'ylgn9': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 104, 55), (0, 69, 41)], 'ylgnbu3': [(237, 248, 177), (127, 205, 187), (44, 127, 184)], 'ylgnbu4': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (34, 94, 168)], 'ylgnbu5': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (44, 127, 184), (37, 52, 148)], 'ylgnbu6': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (44, 127, 184), (37, 52, 148)], 'ylgnbu7': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)], 'ylgnbu8': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)], 'ylgnbu9': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (37, 52, 148), (8, 29, 88)], 'ylorbr3': [(255, 247, 188), (254, 196, 79), (217, 95, 14)], 'ylorbr4': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (204, 76, 2)], 'ylorbr5': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (217, 95, 14), (153, 52, 4)], 'ylorbr6': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (217, 95, 14), (153, 52, 4)], 'ylorbr7': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)], 'ylorbr8': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)], 'ylorbr9': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (153, 52, 4), (102, 37, 6)], 'ylorrd3': [(255, 237, 160), (254, 178, 76), (240, 59, 32)], 'ylorrd4': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (227, 26, 28)], 'ylorrd5': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (240, 59, 32), (189, 0, 38)], 'ylorrd6': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (240, 59, 32), (189, 0, 38)], 'ylorrd7': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)], 'ylorrd8': [(255, 255, 204), (255, 237, 160), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)], } if __name__ == '__main__': main() openxenmanager/window_menuitem.py0000644000175000017500000022352111636446664016074 0ustar rrsrrs # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import gtk from oxcSERVER import * import xtea from thread import * import pdb class oxcWindowMenuItem: """ Class used to manage functions called from menuitems """ # HOST/SERVER def on_m_repair_storage_activate(self, widget, data=None): """ Function called on "Repair storage" """ self.builder.get_object("cancelrepairstorage").set_label("Cancel") self.builder.get_object("lblrepairerror").hide() self.builder.get_object("repairstorage").show() listrepairstorage = self.builder.get_object("listrepairstorage") self.xc_servers[self.selected_host].fill_listrepairstorage(listrepairstorage, self.selected_ref) def on_cancelrepairstorage_clicked(self, widget, data=None): """ Function called when you press cancel on "repair storage" window """ self.builder.get_object("repairstorage").hide() def on_acceptrepairstorage_clicked(self, widget, data=None): """ Function called when you press Repair on "repair storage" window """ self.builder.get_object("lblrepairerror").show() self.builder.get_object("lblrepairerror").set_markup(\ "Repairing... wait please.") listrepairstorage = self.builder.get_object("listrepairstorage") Thread(target=self.xc_servers[self.selected_host].repair_storage, args=(listrepairstorage, self.selected_ref)).start() self.builder.get_object("acceptrepairstorage").set_sensitive(False) def on_m_remove_activate(self, widget, data=None): """ Called from "remove" menuitem of server """ # Remove server from configuration del self.config_hosts[self.selected_name] self.config['servers']['hosts'] = self.config_hosts self.config.write() # Remove from left treeview (treestore) self.treestore.remove(self.selected_iter) def on_m_forget_activate(self, widget, data=None): """ Forget password: dont remember password for server """ # Only put to "" the server password on oxc.conf if self.selected_name in self.config_hosts: self.config_hosts[self.selected_name][1] = "" elif self.selected_ip in self.config_hosts: self.config_hosts[self.selected_ip][1] = "" elif self.selected_host in self.config_hosts: self.config_hosts[self.selected_host][1] = "" def on_m_addserver_activate(self, widget, data=None): """ Add server: show the window for add a new server """ self.builder.get_object("addserver").show() # VM # Make Into Template def on_m_make_into_template_activate(self, widget, data=None): """ Called from "make into template" menuitem of VM Call to method "make_into_template" of oxcSERVER with selected ref param (vm ref) """ self.xc_servers[self.selected_host].make_into_template(self.selected_ref) # Copy VM def on_m_snapshot_activate(self, widget, data=None): """ Called from "snapshot" menuitem of VM Show snapshot dialog and set the name to empty """ self.builder.get_object("snapshotname").set_text("") self.builder.get_object("dialogsnapshotname").show() def on_m_copy_activate(self, widget, data=None): """ Called from "copy" menuitem of VM """ listcopystg = self.builder.get_object("listcopystg") treecopystg = self.builder.get_object("treecopystg") # Set name and description on copy window self.builder.get_object("txtcopyvmname").set_text("Copy of " + self.selected_name) self.builder.get_object("txtcopyvmdesc").set_text( self.xc_servers[self.selected_host].all_vms[self.selected_ref]['name_description'] ) """ Fill the treeview called "treecopystg" with model "listcopystg" with possible storage This treeview is only used on "full copy" fill_listcopystg return the number position of default storage """ defsr = self.xc_servers[self.selected_host].fill_listcopystg(listcopystg, self.selected_host) # Select the default storage treecopystg.set_cursor((defsr,), treecopystg.get_column(0)) treecopystg.get_selection().select_path((defsr, 0)) # Show the window copy window self.builder.get_object("windowcopyvm").show() def on_cancelforcejoinpool_clicked(self, widget, data=None): """ Cancel "force join to pool" dialog """ self.builder.get_object("forcejoinpool").hide() def on_acceptforcejoinpool_clicked(self, widget, data=None): """ Accept "force join to pool" dialog """ last_pool_data = self.xc_servers[self.last_host_pool].last_pool_data self.xc_servers[self.last_host_pool].add_server_to_pool_force(self.selected_ref, last_pool_data) self.builder.get_object("forcejoinpool").hide() def on_m_pool_add_server_activate(self, widget, data=None): """ Called from "Add Server" right menu (pool) """ for i in range(2,len(self.builder.get_object("menu_m_add_server").get_children())): self.builder.get_object("menu_m_add_server").remove(self.builder.get_object("menu_m_add_server").get_children()[2]) for server in self.xc_servers: if self.xc_servers[server].is_connected == True: pool_ref = self.xc_servers[server].all_pools.keys()[0] if self.xc_servers[server].all_pools[pool_ref]["name_label"] == "": image = gtk.Image() image.set_from_file("images/tree_running_16.png") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) # Host ref ref = self.xc_servers[server].all_hosts.keys()[0] self.builder.get_object("menu_m_add_server").append(item) item.connect("activate", self.xc_servers[server].add_server_to_pool, ref, server, ref, self.selected_ip) item.get_children()[0].set_label(self.xc_servers[server].all_hosts[ref]["name_label"]) item.show() def on_m_add_to_pool_activate(self, widget, data=None): """ Called from "Add To pool" menuitem (server) """ for i in range(2,len(self.builder.get_object("menu_add_to_pool").get_children())): self.builder.get_object("menu_add_to_pool").remove(self.builder.get_object("menu_add_to_pool").get_children()[2]) for server in self.xc_servers: if self.xc_servers[server].is_connected == True: pool_ref = self.xc_servers[server].all_pools.keys()[0] if self.xc_servers[server].all_pools[pool_ref]["name_label"] != "": image = gtk.Image() image.set_from_file("images/poolconnected_16.png") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) # Host ref pool = self.xc_servers[server].all_pools[pool_ref]["name_label"] self.builder.get_object("menu_add_to_pool").append(item) item.connect("activate", self.xc_servers[self.selected_ip].add_server_to_pool, pool_ref, self.selected_ip, self.selected_ref, server) item.get_children()[0].set_label(pool) item.show() def on_menuitem_pool_add_server_activate(self, widget, data=None): """ Called from "Add Server" menuitem (pool) """ for i in range(2,len(self.builder.get_object("menu_add_server").get_children())): self.builder.get_object("menu_add_server").remove(self.builder.get_object("menu_add_server").get_children()[2]) for server in self.xc_servers: if self.xc_servers[server].is_connected == True: pool_ref = self.xc_servers[server].all_pools.keys()[0] if self.xc_servers[server].all_pools[pool_ref]["name_label"] == "": image = gtk.Image() image.set_from_file("images/tree_running_16.png") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) # Host ref ref = self.xc_servers[server].all_hosts.keys()[0] self.builder.get_object("menu_add_server").append(item) item.connect("activate", self.xc_servers[server].add_server_to_pool, ref, server, ref, self.selected_ip) item.get_children()[0].set_label(self.xc_servers[server].all_hosts[ref]["name_label"]) item.show() def on_menuitem_server_add_to_pool_activate(self, widget, data=None): """ Called from "Add to pool" menuitem (server) """ for i in range(2,len(self.builder.get_object("menu_server_add_to_pool").get_children())): self.builder.get_object("menu_server_add_to_pool").remove(self.builder.get_object("menu_server_add_to_pool").get_children()[2]) for server in self.xc_servers: if self.xc_servers[server].is_connected == True: pool_ref = self.xc_servers[server].all_pools.keys()[0] if self.xc_servers[server].all_pools[pool_ref]["name_label"] != "": image = gtk.Image() image.set_from_file("images/poolconnected_16.png") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) # Host ref pool = self.xc_servers[server].all_pools[pool_ref]["name_label"] self.builder.get_object("menu_server_add_to_pool").append(item) item.connect("activate", self.xc_servers[self.selected_ip].add_server_to_pool, pool_ref, self.selected_ip, self.selected_ref, server) item.get_children()[0].set_label(pool) item.show() def on_m_resume_on_activate(self, widget, data=None): """ Called from "Resumen on" menuitem of VM """ # Remove the previous possible servers of submenu (right menu) for i in range(2,len(self.builder.get_object("menu_resume_on").get_children())): self.builder.get_object("menu_resume_on").remove(self.builder.get_object("menu_resume_on").get_children()[2]) # Go all servers and add to submenu (right menu) for h in self.xc_servers[self.selected_host].all_hosts: image = gtk.Image() image.set_from_file("images/xen.gif") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) """ Set the signal, when is clicked call to function "start_resumen_on" with params: - Selected vm ref - Host ref """ item.connect("activate", self.xc_servers[self.selected_host].resume_vm_on, self.selected_ref, h) self.builder.get_object("menu_resume_on").append(item) host_name = self.xc_servers[self.selected_host].all_hosts[h]['name_label'] """ Can start function could return: - Empty string means vm can start in that server - Not empty string means means vm cannot start in that server (not memory or other error) """ can_start = self.xc_servers[self.selected_host].can_start(self.selected_ref, h) if can_start: item.get_children()[0].set_label(host_name + " : " + can_start) else: item.get_children()[0].set_label(host_name) item.show() # If server cannot be used to resume on it, disable server if can_start != "": item.set_sensitive(False) def on_m_start_on_activate(self, widget, data=None): """ Called from "Start on" menuitem of VM """ # Remove the previous possible servers of submenu (right menu) for i in range(2,len(self.builder.get_object("menu_start_on").get_children())): self.builder.get_object("menu_start_on").remove(self.builder.get_object("menu_start_on").get_children()[2]) # Go all servers and add to submenu (right menu) for h in self.xc_servers[self.selected_host].all_hosts: image = gtk.Image() image.set_from_file("images/xen.gif") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) """ Set the signal, when is clicked call to function "start_resumen_on" with params: - Selected vm ref - Host ref """ item.connect("activate", self.xc_servers[self.selected_host].start_vm_on, self.selected_ref, h) self.builder.get_object("menu_start_on").append(item) host_name = self.xc_servers[self.selected_host].all_hosts[h]['name_label'] """ Can start function could return: - Empty string means vm can start in that server - Not empty string means means vm cannot start in that server (not memory or other error) """ can_start = self.xc_servers[self.selected_host].can_start(self.selected_ref, h) if can_start: item.get_children()[0].set_label(host_name + " : " + can_start) else: item.get_children()[0].set_label(host_name) item.show() # If server cannot be used to resume on it, disable server if can_start != "": item.set_sensitive(False) def on_m_pool_migrate_activate(self, widget, data=None): """ Called from "Start on" menuitem of VM """ # Remove the previous possible servers of submenu (right menu) for i in range(2,len(self.builder.get_object("menu_pool_migrate").get_children())): self.builder.get_object("menu_pool_migrate").remove(self.builder.get_object("menu_pool_migrate").get_children()[2]) # Go all servers and add to submenu (right menu) for h in self.xc_servers[self.selected_host].all_hosts: image = gtk.Image() image.set_from_file("images/xen.gif") item = gtk.ImageMenuItem(gtk.STOCK_HELP,None) item.use_underline = False item.set_image(image) """ Set the signal, when is clicked call to function "start_resumen_on" with params: - Selected vm ref - Host ref """ item.connect("activate", self.xc_servers[self.selected_host].migrate_vm, self.selected_ref, h) self.builder.get_object("menu_pool_migrate").append(item) host_name = self.xc_servers[self.selected_host].all_hosts[h]['name_label'] resident_on = self.xc_servers[self.selected_host].all_vms[self.selected_ref]['resident_on'] """ Can start function could return: - Empty string means vm can start in that server - Not empty string means means vm cannot start in that server (not memory or other error) """ can_start = self.xc_servers[self.selected_host].can_start(self.selected_ref, h) if can_start: item.get_children()[0].set_label(host_name + " : " + can_start) else: item.get_children()[0].set_label(host_name) item.show() # If server cannot be used to resume on it, disable server if can_start != "" or h == resident_on: item.set_sensitive(False) #TOOLBAR def on_tb_start_clicked(self, widget, data=None): """ "Start" button on toolbar is pressed Power on a VM """ self.xc_servers[self.selected_host].start_vm(self.selected_ref) def on_tb_clean_shutdown_clicked(self, widget, data=None): """ "Clean shutdown" on toolbar is pressed Clean shutdown a vm """ self.xc_servers[self.selected_host].clean_shutdown_vm(self.selected_ref) def on_tb_hard_shutdown_clicked(self, widget, data=None): """ "Hard shutdown" on toolbar is pressed Hard shutdown a vm """ self.xc_servers[self.selected_host].hard_shutdown_vm(self.selected_ref) def on_tb_clean_reboot_clicked(self, widget, data=None): """ "Clean reboot" on toolbar is pressed Clean reboot a vm """ self.xc_servers[self.selected_host].clean_reboot_vm(self.selected_ref) def on_tb_hard_reboot_clicked(self, widget, data=None): """ "Hard reboot" on toolbar is pressed hard reboot a vm """ self.xc_servers[self.selected_host].hard_reboot_vm(self.selected_ref) def on_tb_suspend_clicked(self, widget, data=None): """ "Suspend" on toolbar is pressed Suspend a vm """ self.xc_servers[self.selected_host].suspend_vm(self.selected_ref) def on_tb_unpause_clicked(self, widget, data=None): """ "Resumen" on toolbar is pressed Resume a suspended vm """ self.xc_servers[self.selected_host].unpause_vm(self.selected_ref) def on_tbalerts_clicked(self, widget, data=None): """ Open the alert window """ self.builder.get_object("windowalerts").show() def update_toolbar(self): """ This function is called when a VM, host, storage or template is selected Toolbar buttons are called: tb_action, e.g: tb_start check if "start" (removing tb_) exists on possible actions of this VM/host/... """ toolbar = self.builder.get_object("toolbar") # for each children of toolbar for child in toolbar.get_children(): if gtk.Buildable.get_name(child)[0:3] == "tb_": # self.selected_actions contains possible actions # if not exists: disable button # else: enable button if not self.selected_actions or \ self.selected_actions.count(gtk.Buildable.get_name(child)[3:]) \ == 0: child.set_sensitive(False) else: child.set_sensitive(True) if gtk.Buildable.get_name(child)[3:] == "hard_shutdown": if not self.selected_actions.count("clean_shutdown"): self.builder.get_object("tb_clean_shutdown").hide() self.builder.get_object("tb_hard_shutdown").show() if gtk.Buildable.get_name(child)[3:] == "hard_reboot": if not self.selected_actions.count("clean_reboot"): self.builder.get_object("tb_clean_reboot").hide() self.builder.get_object("tb_hard_reboot").show() if gtk.Buildable.get_name(child)[3:] == "clean_shutdown": self.builder.get_object("tb_clean_shutdown").show() self.builder.get_object("tb_clean_reboot").show() self.builder.get_object("tb_hard_reboot").hide() self.builder.get_object("tb_hard_shutdown").hide() # MENUBAR Actions def on_m_start_activate(self, widget, data=None): """ "Start" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].start_vm(self.selected_ref) def on_m_clean_shutdown_activate(self, widget, data=None): """ "Clean shutdown" menuitem pressed on right click menu """ if self.selected_type == "vm": self.xc_servers[self.selected_host].clean_shutdown_vm(self.selected_ref) elif self.selected_type == "server" or self.selected_type == "host": self.on_menuitem_server_shutdown_activate(widget, data) def on_m_clean_reboot_activate(self, widget, data=None): """ "Clean reboot" menuitem pressed on right click menu """ if self.selected_type == "vm": self.xc_servers[self.selected_host].clean_reboot_vm(self.selected_ref) elif self.selected_type == "server" or self.selected_type == "host": self.on_menuitem_server_reboot_activate(widget, data) def on_m_suspend_activate(self, widget, data=None): """ "Suspend" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].suspend_vm(self.selected_ref) def on_m_unpause_activate(self, widget, data=None): """ "Unpause" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].unpause_vm(self.selected_ref) def on_m_hard_reboot_activate(self, widget, data=None): """ "Hard reboot" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].hard_reboot_vm(self.selected_ref) def on_m_hard_shutdown_activate(self, widget, data=None): """ "Hard shutdown" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].hard_shutdown_vm(self.selected_ref) def on_m_pause_activate(self, widget, data=None): """ "Pause" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].pause_vm(self.selected_ref) def on_m_unsuspend_activate(self, widget, data=None): """ "Resume" (unsuspend) menuitem pressed on right click menu """ self.xc_servers[self.selected_host].unsuspend_vm(self.selected_ref) def on_m_resume_activate(self, widget, data=None): """ "Resume" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].resume_vm(self.selected_ref) def on_menuitem_tools_updatemanager_activate(self, widget, data=None): """ "Update Manager" menuitem pressed on right click menu """ listupdates = self.builder.get_object("listupdates") treeupdates = self.builder.get_object("treeupdates") self.xc_servers[self.selected_host].fill_list_updates(self.selected_ref, listupdates) if listupdates.__len__(): treeupdates.set_cursor((0, ), treeupdates.get_column(0)) treeupdates.get_selection().select_path((0, )) self.builder.get_object("updatemanager").show() def on_installxenservertools_activate(self, widget, data=None): """ "Install XenServer Tools" menuitem pressed on right click menu """ self.xc_servers[self.selected_host].install_xenserver_tools(self.selected_ref) def on_m_forget_activate(self, widget, data=None): """ "Forget Storage" menuitem pressed on right click menu """ target=self.xc_servers[self.selected_host].forget_storage(self.selected_ref) def on_m_unplug_activate(self, widget, data=None): """ "Detach Storage" menuitem pressed on right click menu """ # Show confirmation dialog self.builder.get_object("detachstorage").show() def on_acceptdetachstorage_clicked(self, wwidget, data=None): """ Function called when you accept confirmation "detach storage" dialog """ #target=self.xc_servers[self.selected_host].detach_storage(self.selected_ref) Thread(target=self.xc_servers[self.selected_host].detach_storage, args=(self.selected_ref,)).start() self.builder.get_object("detachstorage").hide() def on_canceldetachstorage_clicked(self, widget, data=None): """ Function called when you cancel confirmation "detach storage" dialog """ self.builder.get_object("detachstorage").hide() def on_m_reattach_activate(self, widget, data=None): """ "Reattach Storage" menuitem pressed on right click menu """ stgtype = self.xc_servers[self.selected_host].all_storage[self.selected_ref]['type'] # If selected type is iso, you only can select "NFS ISO" or "CIFS ISO" if stgtype == "iso": disable = ["radionewstgnfsvhd", "radionewstgiscsi", "radionewstghwhba", "radionewstgnetapp", "radionewstgdell"] for widget in disable: self.builder.get_object(widget).set_sensitive(False) enable = ["radionewstgcifs", "radionewstgnfsiso"] for widget in enable: self.builder.get_object(widget).set_sensitive(True) elif stgtype == "lvmoiscsi": self.builder.get_object("radionewstgiscsi").set_active(True) self.builder.get_object("txtiscsiname").set_text(self.selected_name) self.on_nextnewstorage_clicked(self.builder.get_object("nextnewstorage"), data) self.builder.get_object("previousnewstorage").set_sensitive(False) elif stgtype == "nfs": self.builder.get_object("radionewstgnfsvhd").set_active(True) self.builder.get_object("txtnewstgnfsname").set_text(self.selected_name) self.on_nextnewstorage_clicked(widget, data) self.builder.get_object("previousnewstorage").set_sensitive(False) else: print stgtype self.builder.get_object("radionewstgcifs").set_active(True) # Flag variable to know if we will do a reattach self.reattach_storage = True self.builder.get_object("newstorage").show() def on_m_importvm_activate(self, widget, data=None): """ "Import VM" menuitem pressed on right click menu """ blue = gtk.gdk.color_parse("#d5e5f7") # Disable "next button", it will be enabled when file is selected self.builder.get_object("nextvmimport").set_sensitive(False) self.builder.get_object("eventimport0").modify_bg(gtk.STATE_NORMAL, blue) # Set a filter, you only can selected *.xva files self.builder.get_object("filefilterimportvm").add_pattern("*.xva") # Show the import window self.builder.get_object("vmimport").show() # listimportservers contains the connected servers listimportservers = self.builder.get_object("listimportservers") listimportservers.clear() # For each host in config.. for host in self.config_hosts: # If we are connected to this server if host in self.xc_servers: # Then add to list listimportservers.append([gtk.gdk.pixbuf_new_from_file("images/tree_connected_16.png"), self.xc_servers[host].hostname,True,host]); """ else: listimportservers.append([gtk.gdk.pixbuf_new_from_file("images/tree_disconnected_16.png"), host,False]); """ # If we are connected to some server.. if listimportservers.__len__(): treeimportservers = self.builder.get_object("treeimportservers") # Then selected the first treeimportservers.set_cursor((0, ), treeimportservers.get_column(0)) treeimportservers.get_selection().select_path((0, )) def on_m_export_activate(self, widget, data=None): """ "Export VM" menuitem pressed on right click menu """ # Set default name self.filesave.set_current_name(self.selected_name + ".xva") # Show the choose dialog self.filesave.show() def on_m_snap_newvm_activate(self, widget, data=None): """ "New VM From snapshot" menuitem pressed on "snapshot" menu (Snapshots tab of VM) """ # Show the "new vm" window # TODO -> select vm with name_label self.on_m_newvm_activate(widget, data) def on_m_snap_createtpl_activate(self, widget, data=None): """ "Create template from snapshot" menuitem pressed on "snapshot" menu (Snapshots tab of VM) """ # set a default name self.builder.get_object("snaptplname").set_text("Template from snapshot '" + \ self.xc_servers[self.selected_host].all_vms[self.selected_snap_ref]['name_label'] + "'") # Shows a dialog to enter a name for new template self.builder.get_object("dialogsnaptplname").show() def on_m_snap_delete_activate(self, widget, data=None): """ "Delete snapshot" menuitem pressed on "snapshot" menu (Snapshots tab of VM) """ # Show a dialog asking confirmation self.builder.get_object("dialogsnapshotdelete").show() def on_m_destroy_activate(self, widget, data=None): """ "Destroy" menuitem pressed on right click menu (VM) """ # Show a dialog asking confirmation if self.selected_type == "vm": self.builder.get_object("dialogdeletevm").show() self.builder.get_object("dialogdeletevm").set_markup("Are you sure you want to delete VM '" + self.selected_name + "' ?") elif self.selected_type == "template" or self.selected_type == "custom_template": self.builder.get_object("dialogdeletevm").show() self.builder.get_object("dialogdeletevm").set_markup("Are you sure you want to delete template '" + self.selected_name + "' ?") elif self.selected_type == "storage": print "delete storage" #self.treestore.remove(self.selected_iter) #self.xc_servers[self.selected_host].destroy_vm(self.selected_ref) def on_m_connect_activate(self, widget, data=None): """ "Connect" menuitem pressed on right click menu (Host) """ # Checks if exists a "master password" # Master password if need to save reverse passwords with XTEA # XTEA is a block cipher to save server password on oxc.conf # If master password if used (saved on oxc.conf as md5) use it to xtea decrypt if not self.selected_name in self.config_hosts: return if len(self.config_hosts[self.selected_name]) > 2: self.builder.get_object("checksslconnection").set_active(str(self.config_hosts[self.selected_name][2]) == "True") if self.password and self.config_hosts[self.selected_name][1]: # Decrypt password to plain # Use typed master password (previously checked with md5) # Fill characters left with "X" to reach a 16 characters decrypt_pw = xtea.crypt("X" * (16-len(self.password)) + self.password, \ self.config_hosts[self.selected_name][1].decode("hex"), self.iv) # Call to add server with name, ip and decrypted password # Add server try to connect to the server self.add_server(self.selected_name, self.config_hosts[self.selected_name][0], \ decrypt_pw) else: # If master password is not set or server hasn't a saved password # Empty entries self.builder.get_object("addserverhostname").get_child().set_text(self.selected_name) self.builder.get_object("addserverusername").set_text(self.config_hosts[self.selected_name][0]) self.builder.get_object("addserverpassword").set_text("") # Show the add server window addserver = self.builder.get_object("addserver").show_all() self.builder.get_object("addserverpassword").grab_focus() def on_m_disconnect_activate(self, widget, data=None): """ "Disconnect" menuitem pressed on right click menu (Host) """ # Checks if exists a "master password" # get the ip/host (not virtual name) host = self.xc_servers[self.selected_host].host # Logout implies: # - Unregister events to current session # - Disconnect of server self.xc_servers[self.selected_host].logout() # Remove from list (and children) if len(self.treestore.get_path(self.selected_iter)) == 2: self.treestore.remove(self.selected_iter) else: path = (self.treestore.get_path(self.selected_iter)[0], self.treestore.get_path(self.selected_iter)[1]) iter = self.treestore.get_iter(path) self.treestore.remove(iter) # Add again the ip/host name self.treestore.append(self.treeroot, ([gtk.gdk.pixbuf_new_from_file("images/tree_disconnected_16.png"), host, None, "server", "Disconnected", None, None, ["connect", "forgetpw", "remove"], None])) # If copy window is showed.. hide self.builder.get_object("windowcopyvm").hide() self.treeview.set_cursor((0, ), self.treeview.get_column(0)) self.treeview.get_selection().select_path((0, )) # Update tabs self.selected_type = "home" self.update_tabs() # Delete alerts self.builder.get_object("listalerts").clear() for host in self.xc_servers: if self.xc_servers[host].is_connected: self.xc_servers[host].fill_alerts(self.listalerts) self.update_n_alerts() def on_m_newvm_activate(self, widget, data=None): """ "New VM" menuitem pressed on right click menu (Host) """ # self.newvmdata is used to set "new vm" parameters self.newvmdata = {} listtemplates = self.builder.get_object("listtemplates") # Fill the "list of templates" to create a new VM self.xc_servers[self.selected_host].fill_list_templates(listtemplates) # Set to first page and setting "page_comple" next button is enabled self.builder.get_object("tabboxnewvm").set_current_page(0) # Select the first template by default treetemplates = self.builder.get_object("treetemplates") treetemplates.set_cursor((0, 1), treetemplates.get_column(1)) treetemplates.get_selection().select_path((0, 1)) # For some templates is needed use DVD Drive or ISO Images # Fill the possible iso images to use self.xc_servers[self.selected_host].fill_list_isoimages(self.listisoimage) self.builder.get_object("radiobutton3_data").set_active(1) # Fill the connected DVDS self.xc_servers[self.selected_host].fill_list_phydvd(self.listphydvd) # Default interfaces for the new vm, and set the first parameter: the number of the interfaces self.xc_servers[self.selected_host].fill_list_networks( self.listnetworks, self.listnetworkcolumn) listnewvmhosts = self.builder.get_object("listnewvmhosts") treenewvmhosts = self.builder.get_object("treenewvmhosts") # A new vm could be started on some host (e.g a pool with servers) # Fill the possible hosts where vm could be start path = self.xc_servers[self.selected_host].fill_listnewvmhosts(listnewvmhosts) # Set the default server treenewvmhosts.set_cursor((path,1), treenewvmhosts.get_column(0)) treenewvmhosts.get_selection().select_path((path,1)) # Setting a default options self.newvmdata['location'] = "radiobutton1" self.newvmdata['vdi'] = "" self.builder.get_object("lblnewvm0").set_markup(' %-35s' % "Template") labels = ["Name", "Location", "Home Server", "CPU / Memory", "Virtual disks", "Virtual Interfaces", "Finish"] for i in range(1, 8): self.builder.get_object("lblnewvm" + str(i)).set_markup( " %-35s" % labels[i-1]) # Show the "new vm" assistent self.newvm.show() # MENUBAR checks def on_checksavepassword_toggled(self, widget, data=None): self.builder.get_object("label259").set_sensitive(widget.get_active()) self.builder.get_object("txtmasterpassword").set_sensitive(widget.get_active()) def on_checkshowxtpls_toggled(self, widget, data=None): """ Enable or disable show templates on left tree """ # Save enable or disable to configuration self.config["gui"]["show_xs_templates"] = widget.get_active() self.config.write() # Call to "refilter" to hide/show the templates self.modelfilter.refilter() def on_checkshowhiddenvms_toggled(self, widget, data=None): """ Enable or disable show templates on left tree """ # Save enable or disable to configuration self.config["gui"]["show_hidden_vms"] = widget.get_active() self.config.write() # Call to "refilter" to hide/show the templates self.modelfilter.refilter() def on_checkshowtoolbar_toggled(self, widget, data=None): """ Enable or disable show top toolbar """ self.config["gui"]["show_toolbar"] = widget.get_active() # If is active, show the toolbar, else hide the toolbar if widget.get_active(): self.builder.get_object("toolbar").show() else: self.builder.get_object("toolbar").hide() # Save in configuration self.config.write() def on_checkshowcustomtpls_toggled(self, widget, data=None, a=None): """ Enable or disable show custom templates on left tree """ self.config["gui"]["show_custom_templates"] = widget.get_active() # Save in configuration self.config.write() # Call to "refilter" to hide/show custom templates self.modelfilter.refilter() def on_checkshowlocalstorage_toggled(self, widget, data=None, a=None): """ Enable or disable show local storage on left tree """ self.config["gui"]["show_local_storage"] = widget.get_active() # Save in configuration self.config.write() # Call to "refilter" to hide/show custom templates self.modelfilter.refilter() # MENUBAR def on_menuitem_entermaintenancemode_activate(self, widget, data=None): """ "Enter Maintenance Mode" on menuitem is pressed """ listmaintenancemode = self.builder.get_object("listmaintenancemode") self.xc_servers[self.selected_host].fill_vms_which_prevent_evacuation(self.selected_ref, listmaintenancemode) self.builder.get_object("maintenancemode").show() def on_cancelmaintenancemode_clicked(self, widget, data=None): """ Pressed "Cancel" button on maintenance window """ self.builder.get_object("maintenancemode").hide() def on_acceptmaintenancemode_clicked(self, widget, data=None): """ Pressed "Accept" button on maintenance window """ self.xc_servers[self.selected_host].enter_maintancemode(self.selected_ref) self.builder.get_object("maintenancemode").hide() def on_menuitem_exitmaintenancemode_activate(self, widget, data=None): """ "Exit Maintenance Mode" on menuitem is pressed """ self.xc_servers[self.selected_host].exit_maintancemode(self.selected_ref) def on_menuitem_vm_startrecovery_activate(self, widget, data=None): """ "Start" button on menuitem is pressed Power on a VM """ self.xc_servers[self.selected_host].start_vm_recovery_mode(self.selected_ref) def on_menuitem_stg_new_activate(self, widget, data=None): """ "New Storage Repository" menuitem pressed on menubar """ blue = gtk.gdk.color_parse("#d5e5f7") # Disable "next button", it will be enabled when file is selected enable= ["radionewstgnfsvhd", "radionewstgiscsi", "radionewstghwhba", "radionewstgnetapp", "radionewstgdell", "radionewstgcifs", "radionewstgnfsiso"] for widget in enable: self.builder.get_object(widget).set_sensitive(True) self.reattach_storage = False self.builder.get_object("nextnewstorage").set_sensitive(True) self.builder.get_object("eventnewstg0").modify_bg(gtk.STATE_NORMAL, blue) self.builder.get_object("tabboxnewstorage").set_current_page(0) self.builder.get_object("newstorage").show() def on_menuitem_dmesg_activate(self, widget, data=None): dmesg = self.xc_servers[self.selected_host].get_dmesg(self.selected_ref) self.builder.get_object("txthostdmesg").get_buffer().set_text(dmesg) self.builder.get_object("hostdmesg").show() def on_management_activate(self, widget, data=None): """ "Management interfaces" on server menu is rpressed """ listmgmtinterfaces = self.builder.get_object("listmgmtinterfaces") treemgmtinterfaces = self.builder.get_object("treemgmtinterfaces") # Fill the list of interfaces with "Management" option enabled self.xc_servers[self.selected_host].fill_mamagement_ifs_list(listmgmtinterfaces) # Set the top label with server selected lblmgmtinterfaces = self.builder.get_object("lblmgmtinterfaces") lblmgmtinterfaces.set_text(lblmgmtinterfaces.get_text().replace("{0}", self.selected_name)) # Show the window self.builder.get_object("mgmtinterface").show() # Select the first interface by default selection = treemgmtinterfaces.get_selection() treemgmtinterfaces.set_cursor((0, ), treemgmtinterfaces.get_column(0)) treemgmtinterfaces.get_selection().select_path((0, )) # Get the reference of default interface pif_ref = listmgmtinterfaces.get_value(selection.get_selected()[1],0) combomgmtnetworks = self.builder.get_object("combomgmtnetworks") listmgmtnetworks = self.builder.get_object("listmgmtnetworks") # Get all information for this PIF pif = self.xc_servers[self.selected_host].all_pif[pif_ref] # Fill the network combo with possible networks # fill_management_networks return the position where network reference of pif is located current = self.xc_servers[self.selected_host].fill_management_networks(listmgmtnetworks, pif['network']) # Set in combo the network for default PIF combomgmtnetworks.set_active(current) # If interface configuration is dhcp disable ip/mask/gw entries if pif['ip_configuration_mode'] == "DHCP": self.builder.get_object("txtmgmtip").set_sensitive(False) self.builder.get_object("txtmgmtmask").set_sensitive(False) self.builder.get_object("txtmgmtgw").set_sensitive(False) # Although could be disabled, set the ip/netmask/gateway self.builder.get_object("txtmgmtip").set_text(pif['IP']) self.builder.get_object("txtmgmtmask").set_text(pif['netmask']) self.builder.get_object("txtmgmtgw").set_text(pif['gateway']) # If ip configuration is with dhcp set appropiate radio enabled self.builder.get_object("radiomgmtipdhcp").set_active(pif['ip_configuration_mode'] == "DHCP") self.builder.get_object("radiomgmtipmanual").set_active(pif['ip_configuration_mode'] != "DHCP") # If dns configuration is with dhcp set appropiate radio enabled self.builder.get_object("radiomgmtdnsdhcp").set_active(pif['DNS'] == "") self.builder.get_object("radiomgmtdnsmanual").set_active(pif['DNS'] != "") # If dns is manual.. if pif['DNS']: # Fill the entries with dns ips dns = pif['DNS'].split(",") self.builder.get_object("txtmgmtdns1").set_text(dns[0]) if len(dns) > 1: self.builder.get_object("txtmgmtdns2").set_text(dns[1]) else: self.builder.get_object("txtmgmtdns2").set_text("") else: # If not, empty the entris and disable both entries self.builder.get_object("txtmgmtdns1").set_sensitive(False) self.builder.get_object("txtmgmtdns2").set_sensitive(False) self.builder.get_object("txtmgmtdns1").set_text("") self.builder.get_object("txtmgmtdns2").set_text("") def on_menuitem_stg_default_activate(self, widget, data=None): """ "Set as Default Storage Repository" menu item is pressed (storage menu) """ self.xc_servers[self.selected_host].set_default_storage(self.selected_ref) def on_menuitem_tools_statusreport_activate(self, widget, data=None): """ "Status report" menu item is pressed (tools menu) """ self.builder.get_object("statusreport").show() listreport = self.builder.get_object("listreport") self.xc_servers[self.selected_host].fill_list_report(self.selected_ref, listreport) self.update_report_total_size_time() def on_menuitem_tools_cad_activate(self, widget, data=None): """ "Send Ctrl-Alt-Del" menu item is pressed (tools menu) """ self.tunnel.send_data("\xfe\x01\x00\x00\x00\x00\x00\x1d") self.tunnel.send_data("\xfe\x01\x00\x00\x00\x00\x00\x38") self.tunnel.send_data("\xfe\x01\x00\x00\x00\x00\x00\xd3") self.tunnel.send_data("\xfe\x00\x00\x00\x00\x00\x00\x1d") self.tunnel.send_data("\xfe\x00\x00\x00\x00\x00\x00\x38") self.tunnel.send_data("\xfe\x00\x00\x00\x00\x00\x00\xd3") def on_menuitem_migratetool_activate(self, widget, data=None): """ "Migrate tool" menu item is pressed (tools menu) """ self.builder.get_object("spinmigratemem").set_value(256) self.builder.get_object("spinmigratevcpus").set_value(1) self.builder.get_object("checkmigrateoutputserver").set_sensitive(self.selected_type == "host") self.builder.get_object("migratetool").show() def on_menuitem_takescreenshot_activate(self, widget, data=None): """ "Take screenshot" menu item is pressed (tools menu) """ self.builder.get_object("savescreenshot").set_current_name("Screenshot_%s.jpg" \ % self.selected_name.replace('/', '_')) self.builder.get_object("savescreenshot").show() def on_cancelsavescreenshot_clicked(self, widget, data=None): self.builder.get_object("savescreenshot").hide() def on_acceptsavescreenshot_clicked(self, widget, data=None): filename = self.builder.get_object("savescreenshot").get_filename() if self.selected_type == "vm": self.xc_servers[self.selected_host].save_screenshot(self.selected_ref, filename) else: #host ref = self.xc_servers[self.selected_host].host_vm[self.selected_ref][0] self.xc_servers[self.selected_host].save_screenshot(ref, filename) self.builder.get_object("savescreenshot").hide() def on_menuitem_options_activate(self, widget, data=None): """ "Options" menu item is pressed (tools menu) """ # Enable/disable the save password option self.builder.get_object("checksavepassword").set_active(eval(self.config["gui"]["save_password"])) # Show the options dialog self.builder.get_object("dialogoptions").show() def on_menuitem_delete_activate(self, widget, data=None): """ "Delete" menu item is pressed (only for Pool) """ # Delete the pool self.xc_servers[self.selected_host].delete_pool(self.selected_ref) def on_menuitem_connectall_activate(self, widget, data=None): """ "Connect all" menu item is pressed (server menu) """ # For each server: connect # TODO: fix self.treestore.foreach(self.foreach_connect, True) def on_menuitem_disconnectall_activate(self, widget, data=None): # For each server: disconnect """ "Disconnect all" menu item is pressed (server menu) """ # For each server: disconnect # TODO: fix self.treestore.foreach(self.foreach_connect, False) def on_collapsechildren_activate(self, widget, data=None): """ "Collapse Children" menu item is pressed """ for child in range(0,self.treestore.iter_n_children(self.selected_iter)): iter = self.treestore.iter_nth_child(self.selected_iter, child) if self.treestore.iter_n_children(iter): path = self.treestore.get_path(iter) self.treeview.collapse_row(path) def on_expandall_activate(self, widget, data=None): """ "Expand all" menu item is pressed """ for child in range(0,self.treestore.iter_n_children(self.selected_iter)): iter = self.treestore.iter_nth_child(self.selected_iter, child) if self.treestore.iter_n_children(iter): path = self.treestore.get_path(iter) self.treeview.expand_row(path, True) def on_menuitem_changepw_activate(self, widget, data=None): """ "Change Server Password" menu item is pressed """ self.builder.get_object("lblwrongpw").hide() self.builder.get_object("changepassword").show() self.builder.get_object("txtcurrentpw").set_text("") self.builder.get_object("txtnewpw").set_text("") self.builder.get_object("txtrenewpw").set_text("") self.builder.get_object("acceptchangepassword").set_sensitive(False) label = self.builder.get_object("lblchangepw").get_label() self.builder.get_object("lblchangepw").set_label(label.replace("{0}", self.selected_name)) def on_menuitem_install_xslic_activate(self, widget, data=None): """ "Install License Key" menu item is pressed """ # Show file chooser if self.xc_servers[self.selected_host].all_hosts[self.selected_ref].get("license_server"): licenserver = self.xc_servers[self.selected_host].all_hosts[self.selected_ref].get("license_server") self.builder.get_object("licensehost").set_text(licenserver["address"]) self.builder.get_object("licenseport").set_text(licenserver["port"]) self.builder.get_object("dialoglicensehost").show() else: self.builder.get_object("filterfilelicensekey").add_pattern("*.xslic") self.builder.get_object("filelicensekey").show() def on_cancellicensehost_clicked(self, widget, data=None): """ Function called when you press cancel on license host dialog """ self.builder.get_object("dialoglicensehost").hide() def on_acceptlicensehost_clicked(self, widget, data=None): """ Function called when you press cancel on license host dialog """ edition = "advanced" for licwidget in ["advanced", "enterprise", "platinum", "enterprise-xd"]: if self.builder.get_object(licwidget).get_active(): edition = licwidget break licensehost = self.builder.get_object("licensehost").get_text() licenseport = self.builder.get_object("licenseport").get_text() self.xc_servers[self.selected_host].set_license_host(self.selected_ref, licensehost, licenseport, edition) self.builder.get_object("dialoglicensehost").hide() def on_cancelfilelicensekey_clicked(self, widget, data=None): """ Function called when you press cancel on filchooser "install license key" """ # Hide the file chooser self.builder.get_object("filelicensekey").hide() def on_openfilelicensekey_clicked(self, widget, data=None): """ Function called when you press open on filchooser "install license key" """ filename = self.builder.get_object("filelicensekey").get_filename() self.xc_servers[self.selected_host].install_license_key(self.selected_ref, filename) #print open( self.builder.get_object("filelicensekey").get_filename(), "rb").read().encode("base64").replace("\n","") # Hide the file chooser self.builder.get_object("filelicensekey").hide() def on_menuitem_restoreserver_activate(self, widget, data=None): """ "Restoreserver" menu item is pressed """ # Show select destination dialog self.builder.get_object("filefilterrestoreserver").add_pattern("*.xbk") self.builder.get_object("filerestoreserver").show() def on_menuitem_backupserver_activate(self, widget, data=None): """ "Backup server" menu item is pressed """ # Show select destination dialog filebackupserver = self.builder.get_object("filebackupserver") filebackupserver.set_current_name(self.selected_name + ".xbk") self.builder.get_object("filefilterbackupserver").add_pattern("*.xbk") self.builder.get_object("filebackupserver").show() def on_menuitem_downloadlogs_activate(self, widget, data=None): """ "Download logs" (host) menu item is pressed """ # Show select destination dialog filedownloadlogs = self.builder.get_object("filedownloadlogs") filedownloadlogs.set_current_name(self.selected_name + ".tar.gz") self.builder.get_object("filedownloadlogs").show() def on_cancelfilebackupserver_clicked(self, widget, data=None): """ Function called when you cancel dialog for save backup server """ self.builder.get_object("filebackupserver").hide() def on_savefilebackupserver_clicked(self, widget, data=None): """ Function called when you accept dialog for save backup server """ filebackupserver = self.builder.get_object("filebackupserver") filename = filebackupserver.get_filename() self.xc_servers[self.selected_host].thread_backup_server(self.selected_ref, filename, self.selected_name) self.builder.get_object("filebackupserver").hide() def on_cancelfiledownloadlogs_clicked(self, widget, data=None): """ Function called when you cancel dialog for download logs """ self.builder.get_object("filedownloadlogs").hide() def on_savefiledownloadlogs_clicked(self, widget, data=None): """ Function called when you accept dialog for download logs """ filedownloadlogs = self.builder.get_object("filedownloadlogs") filename = filedownloadlogs.get_filename() self.xc_servers[self.selected_host].thread_host_download_logs(self.selected_ref, filename, self.selected_name) self.builder.get_object("filedownloadlogs").hide() def on_cancelrestoreserver_clicked(self, widget, data=None): """ Function called when you cancel dialog for open file to restore server """ self.builder.get_object("filerestoreserver").hide() def on_openfilerestoreserver_clicked(self, widget, data=None): """ Function called when you accept dialog for open file to restore server """ filename = self.builder.get_object("filerestoreserver").get_filename() self.xc_servers[self.selected_host].thread_restore_server(self.selected_ref, filename, self.selected_name) self.builder.get_object("filerestoreserver").hide() def on_menuitem_server_reboot_activate(self, widget, data=None): """ "Reboot server" menu item is pressed """ self.builder.get_object("confirmreboot").show() def on_cancelconfirmreboot_clicked(self, widget, data=None): """ Function called when you cancel dialog for reboot server """ self.builder.get_object("confirmreboot").hide() def on_acceptconfirmreboot_clicked(self, widget, data=None): """ Function called when you cancel dialog for reboot server """ res = self.xc_servers[self.selected_host].reboot_server(self.selected_ref) #res = "OK" if res == "OK": self.on_m_disconnect_activate(widget, data) self.builder.get_object("confirmreboot").hide() def on_menuitem_server_shutdown_activate(self, widget, data=None): """ "Reboot server" menu item is pressed """ self.builder.get_object("confirmshutdown").show() def on_acceptconfirmshutdown_clicked(self, widget, data=None): """ "Reboot server" menu item is pressed """ res = self.xc_servers[self.selected_host].shutdown_server(self.selected_ref) if res == "OK": self.on_m_disconnect_activate(widget, data) self.builder.get_object("confirmshutdown").hide() def on_cancelconfirmshutdown_clicked(self, widget, data=None): """ Function called when you cancel dialog for shutdown server """ self.builder.get_object("confirmshutdown").hide() def on_menuitem_checkforupdates_activate(self, widget, data=None): """ "Check for Updates" menu item is pressed (help) """ pool = [] hotfix = [] # Get pool and patch info for server in self.xc_servers.values(): for host in server.all_hosts: pool.append("pool_" + server.all_hosts[host]["software_version"]["product_version"] + "=1") for patch in server.all_hosts[host]["patches"]: host_patch = server.all_host_patch[patch] if host_patch["applied"]: hotfix.append("hotfix_" + server.all_pool_patch[host_patch["pool_patch"]]["uuid"] + "=1") else: hotfix.append("hotfix_" + server.all_pool_patch[host_patch["pool_patch"]]["uuid"] + "=0") url = "http://updates.xensource.com/XenServer/5.5.2/XenCenter?%s;%s" % (";".join(pool), ";".join(hotfix)) import webbrowser webbrowser.open(url) def on_menuitem_xenserver_on_the_web_activate(self, widget, data=None): """ "Xenserver on the web" menu item is pressed (help) """ url = "www.xenserver.com" import webbrowser webbrowser.open(url) def on_menuitem_help_activate(self, widget, data=None): """ "About" menu item is pressed (Help) """ # Show about dialog self.builder.get_object("aboutdialog").show() def on_menuitem_pool_remove_server_activate(self, widget, data=None): """ "Remove server" (from pool) menu item is pressed (pool) """ self.last_dialog_label = self.builder.get_object("removeserverfrompool").get_property("text") label = self.builder.get_object("removeserverfrompool").get_property("text") pool_ref = self.xc_servers[self.selected_host].all_pools.keys()[0] self.builder.get_object("removeserverfrompool").set_markup( label.replace("{0}", self.selected_name).replace("{1}", self.xc_servers[self.selected_host].all_pools[pool_ref]["name_label"]) ) self.builder.get_object("removeserverfrompool").show() def on_acceptremoveserverfrompool_clicked(self, widget, data=None): """ Function called when you accept remove server from pool """ Thread(target=self.xc_servers[self.selected_host].remove_server_from_pool, args=(self.selected_ref,)).start() self.builder.get_object("removeserverfrompool").hide() self.builder.get_object("removeserverfrompool").set_markup(self.last_dialog_label) def on_cancelremoveserverfrompool_clicked(self, widget, data=None): """ Function called when you accept remove server from pool """ self.builder.get_object("removeserverfrompool").hide() self.builder.get_object("removeserverfrompool").set_markup(self.last_dialog_label) def on_menuitem_pool_backupdb_activate(self, widget, data=None): """ "Backup database" menu item is pressed(pool) """ self.builder.get_object("filterfilepoolbackupdb").add_pattern("*.xml") filepoolbackupdb = self.builder.get_object("filepoolbackupdb") filepoolbackupdb.set_current_name(self.selected_name + "_backup_db.xml") filepoolbackupdb.show() def on_cancelfilepoolbackupdb_clicked(self, widget, data=None): """ "Cancel" press on file chooser dialog for database pool backup """ self.builder.get_object("filepoolbackupdb").hide() def on_acceptfilepoolbackupdb_clicked(self, widget, data=None): """ "Cancel" press on file chooser dialog for database pool backup """ filename = self.builder.get_object("filepoolbackupdb").get_filename() self.xc_servers[self.selected_host].pool_backup_database(self.selected_ref, filename, self.selected_name) self.builder.get_object("filepoolbackupdb").hide() def on_rebootconfirmpoolrestoredb_clicked(self, widget, data=None): """ "Reboot" press on dialog restore database pool (reboot/dry run/cancel) """ self.builder.get_object("confirmpoolrestoredb").hide() filename = self.builder.get_object("filepoolrestoredb").get_filename() Thread(target=self.xc_servers[self.selected_host].pool_restore_database, \ args=(self.selected_ref, filename, self.selected_name, "false")).start() self.builder.get_object("filepoolrestoredb").hide() def on_dryrunconfirmpoolrestoredb_clicked(self, widget, data=None): """ "Dry run" press on dialog restore database pool (reboot/dry run/cancel) """ self.builder.get_object("confirmpoolrestoredb").hide() filename = self.builder.get_object("filepoolrestoredb").get_filename() Thread(target=self.xc_servers[self.selected_host].pool_restore_database, \ args=(self.selected_ref, filename, self.selected_name, "true")).start() self.builder.get_object("filepoolrestoredb").hide() def on_cancelconfirmpoolrestoredb_clicked(self, widget, data=None): """ "Dry run" press on dialog restore database pool (reboot/dry run/cancel) """ self.builder.get_object("confirmpoolrestoredb").hide() self.builder.get_object("filepoolbackupdb").hide() def on_menuitem_pool_restoredb_activate(self, widget, data=None): """ "Restore database" menu item is pressed(pool) """ self.builder.get_object("filepoolrestoredb").show() def on_cancelfilepoolrestoredb_clicked(self, widget, data=None): """ "Cancel" press on file chooser dialog for database pool restore """ self.builder.get_object("filepoolrestoredb").hide() def on_acceptfilepoolrestoredb_clicked(self, widget, data=None): """ "Open" press on file chooser dialog for database pool restore """ self.builder.get_object("confirmpoolrestoredb").show() self.builder.get_object("filepoolrestoredb").hide() def on_menuitem_pool_disconnect_activate(self, widget, data=None): """ "Disconnect" (from pool) menu item is pressed """ self.on_m_disconnect_activate(widget, data) def on_menuitem_pool_new_activate(self, widget, data=None): """ "New Pool..." menu item is pressed """ listpoolmaster = self.builder.get_object("listpoolmaster") listpoolmaster.clear() combopoolmaster = self.builder.get_object("combopoolmaster") # For each server add to combobox master servers list for host in self.config_hosts.keys(): # If server is connected.. if host in self.xc_servers: # Add to combo pool = False for pool_ref in self.xc_servers[host].all_pools: if self.xc_servers[host].all_pools[pool_ref]['name_label'] != "": pool = True if not pool: listpoolmaster.append([host, self.xc_servers[host].hostname]) # Set the first as default combopoolmaster.set_active(0) ref = None # If there are servers added to combobox, get the ref if combopoolmaster.get_active_iter(): ref = listpoolmaster.get_value(combopoolmaster.get_active_iter(), 0) listpoolvms = self.builder.get_object("listpoolvms") listpoolvms.clear() # For each server add to possible servers for pool for host in self.config_hosts.keys(): if host not in self.xc_servers: listpoolvms.append([None, host, 0, "Disconnected", False]) else: if self.xc_servers[host].is_connected: pool = False for pool_ref in self.xc_servers[host].all_pools: if self.xc_servers[host].all_pools[pool_ref]['name_label'] != "": pool = True if not pool: if ref != host: listpoolvms.append([host, self.xc_servers[host].hostname, False, "", True]) else: listpoolvms.append([host, self.xc_servers[host].hostname, True, "Master", False]) else: listpoolvms.append([host, self.xc_servers[host].hostname, False, "This server is already in a pool", False]) else: listpoolvms.append([None, host, 0, "Disconnected", False]) # Show the "newpool" window self.builder.get_object("newpool").show() def update_menubar(self): """ This function is called when a VM, host, storage or template is selected Depends if you selected a server, host (server connected), vm then A menuitems are enabled and others are disabled """ show = {} if self.selected_type == "pool": show["menu5"] = ["menuitem_pool_new", "menuitem_pool_delete", "menuitem_pool_disconnect","menuitem_pool_prop", "menuitem_pool_backupdb", "menuitem_pool_restoredb", "menuitem_pool_add_server"] # TODO: disable menuite_connectall show["menu6"] = ["menuitem_addserver", "menuitem_disconnectall", "menuitem_connectall", "menuitem_forget", "menuitem_remove"] show["menu7"] = ["menuitem_importvm2"] show["menu8"] = [""] show["menu9"] = [""] show["menu10"] = ["menuitem_options", "menuitem_migratetool", "menuitem_tools_updatemanager"] if self.selected_type == "home": show["menu5"] = [""] # TODO: disable menuite_connectall show["menu6"] = ["menuitem_addserver", "menuitem_connectall", "menuitem_disconnectall"] show["menu7"] = ["menuitem_importvm2"] show["menu8"] = [""] show["menu9"] = [""] show["menu10"] = ["menuitem_options","menuitem_tools_alerts", "menuitem_migratetool"] if self.selected_type == "server": if self.selected_state == "Disconnected": show["menu5"] = ["menuitem_pool_new"] # TODO: disable menuite_connectall show["menu6"] = ["menuitem_addserver", "menuitem_disconnectall", "menuitem_connectall", "menuitem_connect", "menuitem_forget", "menuitem_remove"] show["menu7"] = ["menuitem_importvm2"] show["menu8"] = [""] show["menu9"] = [""] show["menu10"] = ["menuitem_options", "menuitem_migratetool"] if self.selected_type == "host": show["menu5"] = ["menuitem_pool_new"] # TODO: use allowed_operations reboot/shutdown show["menu6"] = ["menuitem_addserver", "menuitem_disconnectall", "menuitem_disconnect", "menuitem_forget",\ "menuitem_remove", "menuitem_newvm", "menuitem_server_prop", "menuitem_mgmt_ifs", "menuitem_dmesg",\ "menuitem_server_reboot", "menuitem_server_shutdown", "menuitem_changepw","menuitem_backupserver", \ "menuitem_restoreserver","menuitem_install_xslic","menuitem_server_add_to_pool", \ "menuitem_downloadlogs" ] show["menu7"] = ["menuitem_importvm2", "menuitem_newvm2"] show["menu8"] = ["menuitem_stg_new"] show["menu9"] = ["menuitem_tpl_import"] show["menu10"] = ["menuitem_options","menuitem_tools_alerts", "menuitem_takescreenshot", "menuitem_migratetool", "menuitem_tools_statusreport", "menuitem_tools_updatemanager"] pool_ref = self.xc_servers[self.selected_host].all_pools.keys()[0] if self.xc_servers[self.selected_host].all_hosts[self.selected_ref]["enabled"]: show["menu6"].append("menuitem_entermaintenancemode") else: show["menu6"].append("menuitem_exitmaintenancemode") if self.xc_servers[self.selected_host].all_pools[pool_ref]["name_label"] != '' and \ self.xc_servers[self.selected_host].all_pools[pool_ref]["master"] != self.selected_ref: show["menu5"].append("menuitem_pool_remove_server") if self.selected_type == "vm": show["menu6"] = ["menuitem_newvm", "menuitem_server_prop", "menuitem_mgmt_ifs", "menuitem_addserver", "menuitem_disconnectall"] show["menu7"] = ["menuitem_importvm2", "menuitem_newvm2", "menuitem_vm_prop"] show["menu8"] = ["menuitem_stg_new", "menuitem_stg_newvdi", "menuitem_stg_attachvdi"] show["menu9"] = ["menuitem_tpl_import"] show["menu10"] = ["menuitem_options","menuitem_tools_alerts", "menuitem_takescreenshot", "menuitem_migratetool"] # Special case # If in allowed operations of selected VM exists "start", then add the menu item "start in recovery mode" for op in self.xc_servers[self.selected_host].all_vms[self.selected_ref]['allowed_operations']: show["menu7"].append("menuitem_vm_" + op) if op == "start": show["menu7"].append("menuitem_vm_startrecovery") if self.selected_state == "Running": show["menu7"].append("menuitem_vm_install_xs_tools") if self.selected_type == "storage": show["menu5"] = ["menuitem_pool_new"] show["menu6"] = ["menuitem_addserver", "menuitem_connectall", "menuitem_disconnectall","menuitem_newvm"] show["menu7"] = ["menuitem_importvm2","menuitem_newvm2"] show["menu8"] = ["menuitem_stg_new","menuitem_stg_newvdi", "menuitem_stg_attachvdi"] show["menu9"] = [""] show["menu10"] = ["menuitem_options","menuitem_tools_alerts", "menuitem_migratetool"] if self.xc_servers[self.selected_host].all_storage[self.selected_ref]['allowed_operations'].count("vdi_create")>0: show["menu8"].append("menuitem_stg_default") if self.selected_type == "template": show["menu5"] = ["menuitem_pool_new"] show["menu6"] = ["menuitem_addserver", "menuitem_connectall", "menuitem_disconnectall","menuitem_newvm"] show["menu7"] = ["menuitem_importvm2","menuitem_newvm2"] show["menu8"] = ["menuitem_stg_new","", ""] show["menu9"] = ["menuitem_tpl_newvm", "menuitem_tpl_import", "menuitem_tpl_export", "menuitem_tpl_copy", "menuitem_tpl_delete"] show["menu10"] = ["menuitem_options","menuitem_tools_alerts", "menuitem_migratetool"] # For each menu... for menu in show: # For each child of this menu.. for child in self.builder.get_object(menu).get_children(): # Check if is on "show" variable if show[menu].count(gtk.Buildable.get_name(child)): # If is on: enable menuitem child.set_sensitive(True) else: # Else: disable menuitem child.set_sensitive(False) def on_tm_logwindow_activate(self, widget, data=None): # TODO: fix it URGENT for i in range(1, 1): self.builder.get_object("logwindow").show() vboxframe = gtk.Frame() if i % 2 == 0: vboxframe.set_size_request(500,100) else: vboxframe.set_size_request(500,80) vboxchild = gtk.Fixed() vboxchildlabel1 = gtk.Label() vboxchildlabel2 = gtk.Label() vboxchildlabel3 = gtk.Label() vboxchildlabel4 = gtk.Label() vboxchildlabel5 = gtk.Label() #FIXME #vboxchildprogressbar.set_style(1) vboxchildlabel1.set_label("Starting ... ") vboxchildlabel2.set_label("23:28 04/08/2009") vboxchildlabel3.set_label("Details: problem starting..") vboxchildlabel4.set_label("Time: 00:00:00") vboxchild.put(vboxchildlabel1, 25, 12) vboxchild.put(vboxchildlabel2, 800, 12) vboxchild.put(vboxchildlabel3, 25, 32) vboxchild.put(vboxchildlabel4, 25, 52) # Active task if i % 2 == 0: vboxchildcancel = gtk.Button() vboxchildprogressbar = gtk.ProgressBar() vboxchildprogressbar.set_size_request(800,20) vboxchildprogressbar.set_fraction(float(1/float(i))) vboxchild.put(vboxchildcancel, 800, 32) vboxchildcancel.set_label("Cancel") vboxchildlabel5.set_label("Progress: ") vboxchild.put(vboxchildprogressbar, 100, 72) vboxchild.put(vboxchildlabel5, 25, 72) vboxframe.add(vboxchild) if i % 2 == 0: vboxframe.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("red")) else: vboxframe.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) self.builder.get_object("vboxlog").add(vboxframe) self.builder.get_object("vboxlog").show_all() openxenmanager/window.py0000644000175000017500000023424511636446664014176 0ustar rrsrrs#!/usr/bin/env python # ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import sys # Add paths for compatibility (centos, debian lenny..) sys.path.append("/usr/local/lib/python2.5/site-packages/") sys.path.append("gtk-2.0/") sys.path.append("/usr/local/lib/python2.5/site-packages/gtk-2.0/") import os if os.path.dirname(sys.argv[0]): os.chdir(os.path.dirname(sys.argv[0])) import pygtk # On next releases we will use gettext for translations APP = 'oxc' DIR = 'locale' if sys.platform != "win32" and sys.platform != "darwin": # If sys.platform is linux or unix """ if not os.path.exists("oxcgtkvnc.so"): if platform.architecture()[0] == "32bit": shutil.copy("32/oxcgtkvnc.so", "oxcgtkvnc.so") shutil.copy("32/liboxcgtk-vnc-1.0.so.0", "liboxcgtk-vnc-1.0.so.0") if platform.architecture()[0] == "64bit": shutil.copy("64/oxcgtkvnc.so", "oxcgtkvnc.so") shutil.copy("64/liboxcgtk-vnc-1.0.so.0", "liboxcgtk-vnc-1.0.so.0") """ import gtkvnc # Only needed for translations import gtk.glade gtk.glade.bindtextdomain(APP, DIR) elif sys.platform == "darwin": # On MacOSX with macports sys.platform is "darwin", we need Popen for run tightvnc from subprocess import Popen else: # On Windows we need right tightvnc and we need win32 libraries for move the window from subprocess import Popen import win32gui import win32con import gtk import gobject # For md5 and xtea import hashlib, xtea from oxcSERVER import * from pprint import pprint import signal import atexit import re # For a TreeView Cell with image+text from PixbufTextCellRenderer import PixbufTextCellRenderer #import locale import gettext gettext.install('oxc', localedir="./locale") if sys.platform != "win32": # On unix is recommended run threads from gtk.gdk gtk.gdk.threads_init() else: # On windows is needed run gobject threads gobject.threads_init() # Splitted classes for oxcWindow from window_vm import * from window_host import * from window_properties import * from window_storage import * from window_alerts import * from window_addserver import * from window_newvm import * from window_menuitem import * from window_tools import * import xdot class MyDotWindow(xdot.DotWindow): def __init__(self, window, liststore, treestore): self.liststore = liststore self.treestore = treestore xdot.DotWindow.__init__(self, window) self.widget.connect('button_press_event', self.on_double_clicked) def on_double_clicked(self, widget, event): # On doublie click go to element if event.type == gtk.gdk._2BUTTON_PRESS: x, y = int(event.x), int(event.y) if widget.get_url(x, y): url = widget.get_url(x, y).url # Search ref and go to self.liststore.foreach(self.search_ref, url) return True def search_ref(self, model, path, iter_ref, user_data): if self.liststore.get_value(iter_ref, 6) == user_data: self.treestore.get_selection().select_path(path) event = gtk.gdk.Event(gtk.gdk.BUTTON_RELEASE) event.x = float(-10) event.y = float(-10) self.treestore.emit("button_press_event", event) class oxcWindow(oxcWindowVM,oxcWindowHost,oxcWindowProperties,oxcWindowStorage,oxcWindowAlerts,oxcWindowAddServer,oxcWindowNewVm,oxcWindowMenuItem,oxcWindowTools): """Main class to oxc window""" xc_servers = {} # When you select a element of left tree these variables are filled selected_actions = None selected_ref = None selected_iter= None selected_tab = None selected_host = None selected_type = None selected_widget = None selected_state = None noclosevnc = False # If "Use master password" is enabled, password typed is set on it password = None reattach_storage = False # For New VM newvmdata = {} # On Host -> On general tab: "VMs" label hasn't a fixed width # If host is included on "moved" variable then "VMs" label was moved moved = [] # Flag variable to avoid select signals set_active = False # Flag variable to export snapshot export_snap = False export_snap_vm = False # For windows only hWnd = 0 # Used only for plugins.. delete_pages = [] # Used for pool join force last_host_pool = None # For XTEA only (needs a string with 8 characters) iv = "OXCENTER" # Tunnel VNC tunnel = None # For know if performance images was set performance_updated = False def __init__(self): atexit.register(self.signal_handler) signal.signal(15, self.signal_handler) # Read the configuration from oxc.conf file if sys.platform != "win32": if not os.path.exists(os.path.join(os.path.expanduser("~"), ".config")): os.mkdir(os.path.join(os.path.expanduser("~"), ".config")) if not os.path.exists(os.path.join(os.path.expanduser("~"), ".config", "openxenmanager")): os.mkdir(os.path.join(os.path.expanduser("~"), ".config", "openxenmanager")) dirconfig = os.path.join(os.path.expanduser("~"), ".config", "openxenmanager") pathconfig = os.path.join(os.path.expanduser("~"), ".config", "openxenmanager", "oxc.conf") else: if not os.path.exists(os.path.join(os.path.expanduser("~"), "openxenmanager")): os.mkdir(os.path.join(os.path.expanduser("~"), "openxenmanager")) dirconfig = os.path.join(os.path.expanduser("~"), "openxenmanager") pathconfig = os.path.join(os.path.expanduser("~"), "openxenmanager", "oxc.conf") if not os.path.exists(pathconfig): shutil.copy("oxc.conf", pathconfig) self.config = ConfigObj(pathconfig) self.pathconfig = dirconfig # Read from configuration saved servers if self.config['servers']['hosts']: self.config_hosts = self.config['servers']['hosts'] else: self.config_hosts = {} # Define the glade file self.gladefile = "oxc.glade" self.builder = gtk.Builder() self.builder.set_translation_domain("oxc") # Add the file to gtk.Builder object self.builder.add_from_file(self.gladefile) # Connect Windows and Dialog to delete-event (we want not destroy dialog/window) # delete-event is called when you close the window with "x" button # TODO: csun: eventually it should be possible not to do this: http://stackoverflow.com/questions/4657344/ for widget in self.builder.get_objects(): if isinstance(widget, gtk.Dialog) or \ isinstance(widget, gtk.Window) and gtk.Buildable.get_name(widget) != "window1": widget.connect("delete-event", self.on_delete_event) # Frequent objects self.txttreefilter = self.builder.get_object("txttreefilter") self.listphydvd = self.builder.get_object("listphydvd") self.listisoimage = self.builder.get_object("listisoimage") self.listnetworks = self.builder.get_object("listnewvmnetworks") self.listnetworkcolumn = self.builder.get_object("listnewvmnetworkcolumn") self.window = self.builder.get_object("window1") self.listalerts = self.builder.get_object("listalerts") self.treealerts = self.builder.get_object("treealerts") self.filesave = self.builder.get_object("filesave") self.fileopen = self.builder.get_object("fileopen") self.newvm = self.builder.get_object("window_newvm") self.treeview = self.builder.get_object("treevm") self.treeprop = self.builder.get_object("treeprop") self.listprop = self.builder.get_object("listprop") self.statusbar = self.builder.get_object("statusbar1") self.treesearch = self.builder.get_object("treesearch") """ for i in range(0,7): if self.newvm.get_nth_page(i): self.newvm.set_page_complete(self.newvm.get_nth_page(i), True) """ # Combo's style style = gtk.rc_parse_string(''' style "my-style" { GtkComboBox::appears-as-list = 1 } widget "*" style "my-style" ''') self.builder.connect_signals(self) # Create a new TreeStore self.treestore = gtk.TreeStore(gtk.gdk.Pixbuf, str, str, str, str, str, str, str, str) # Image,Name, uuid, type, state, host, ref, actions, ip # Append default logo on created TreeStore self.treeroot = self.treestore.append(None, ([gtk.gdk.pixbuf_new_from_file("images/xen.gif"), "OpenXenManager", None, "home", "home", None, None, ["addserver","connectall","disconnectall"], None])) # Model Filter is used but show/hide templates/custom templates/local storage.. self.modelfilter = self.treestore.filter_new() # Define the function to check if a element should be showed or not self.modelfilter.set_visible_func(self.visible_func) self.treeview.set_model(self.modelfilter) self.modelfiltertpl = self.builder.get_object("listtemplates").filter_new() self.builder.get_object("treetemplates").set_model(self.modelfiltertpl) self.modelfiltertpl.set_visible_func(self.visible_func_templates) self.builder.get_object("networkcolumn1").set_property("model", self.builder.get_object("listimportnetworkcolumn")) self.builder.get_object("cellrenderercombo1").set_property("model", self.builder.get_object("listnewvmnetworkcolumn")) # Same for properties treestore self.propmodelfilter = self.listprop.filter_new() self.propmodelfilter.set_visible_func(self.prop_visible_func) self.treeprop.set_model(self.propmodelfilter) # Fill defaults selection variables self.selected_name = "OpenXenManager" self.selected_type = "home" self.selected_uuid = "" self.headimage = self.builder.get_object("headimage") self.headlabel = self.builder.get_object("headlabel") self.headlabel.set_label(self.selected_name) self.headimage.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file("images/xen.gif")) if "show_hidden_vms" not in self.config["gui"]: self.config["gui"]["show_hidden_vms"] = "False" self.config.write() # Set menuitem checks to value from configuration self.builder.get_object("checkshowxstpls").set_active(self.config["gui"]["show_xs_templates"] == "True") self.builder.get_object("checkshowcustomtpls").set_active(self.config["gui"]["show_custom_templates"] == "True") self.builder.get_object("checkshowlocalstorage").set_active(self.config["gui"]["show_local_storage"] == "True") self.builder.get_object("checkshowtoolbar").set_active(self.config["gui"]["show_toolbar"] == "True") self.builder.get_object("checkshowhiddenvms").set_active(self.config["gui"]["show_hidden_vms"] == "True") if "maps" in self.config: for check in self.config["maps"]: self.builder.get_object(check).set_active(self.config["maps"][check] == "True") # If "Show toolbar" is checked then show, else hide if self.config["gui"]["show_toolbar"] != "False": self.builder.get_object("toolbar").show() else: self.builder.get_object("toolbar").hide() # Add to left tree the saved servers from configuration for host in self.config_hosts.keys(): self.builder.get_object("listaddserverhosts").append([host]) self.treestore.append(self.treeroot, ([gtk.gdk.pixbuf_new_from_file("images/tree_disconnected_16.png"), host, None, "server", "Disconnected", None, None, ["connect", "forgetpw", "remove"], None])) # Expand left tree and update menubar, tabs and toolbar self.treeview.expand_all() self.update_menubar() self.update_tabs() self.update_toolbar() # Create a TreeStore for SERVER->Search tab self.listsearch = gtk.TreeStore(gtk.gdk.Pixbuf, str, object, str, object, str, str, str, str, str, gtk.gdk.Color) self.treesearch.set_model(self.listsearch) #self.treesearch.get_column(0).set_cell_data_func(self.func_cell_data_treesearch, self.treesearch.get_cell(0)) # Add two columns with image/text from PixBufTextCellRenderer class pbtcell = PixbufTextCellRenderer() pbtcell.set_property('xpad', 15) pbtcell.set_property('ypad', 13) tvc = gtk.TreeViewColumn('CPU Usage', pbtcell, text=3, pixbuf=2, background=10) tvc.set_widget(self.builder.get_object("lbltreesearch6")) self.builder.get_object("lbltreesearch6").show() tvc.set_reorderable(True) tvc.set_sort_column_id(3) self.treesearch.insert_column(tvc, 1) pbtcell = PixbufTextCellRenderer() pbtcell.set_property('xpad', 15) pbtcell.set_property('ypad', 13) tvc = gtk.TreeViewColumn('Used memory', pbtcell, text=5, pixbuf=4, background=10) tvc.set_widget(self.builder.get_object("lbltreesearch7")) tvc.set_reorderable(True) tvc.set_sort_column_id(5) self.treesearch.insert_column(tvc, 2) # ComboBox created from GLADE needs a cellrenderertext # and an attribute defining the column to show combobox = self.builder.get_object("radiobutton2_data") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 0) combobox.set_model(self.listphydvd) combobox = self.builder.get_object("radiobutton3_data") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 0) combobox.add_attribute(cell, 'rise', 2) combobox.add_attribute(cell, 'sensitive', 3) combobox.set_model(self.listisoimage) combobox = self.builder.get_object("treeeditnetwork") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listeditnetwork")) combobox = self.builder.get_object("treeaddnetwork") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listaddnetwork")) combobox.set_active(0) combobox = self.builder.get_object("combostgmode") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("liststgmode")) combobox.set_active(0) combobox = self.builder.get_object("combostgposition") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 0) combobox.set_model(self.builder.get_object("liststgposition")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combomgmtnetworks") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listmgmtnetworks")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combopoolmaster") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listpoolmaster")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combotargetiqn") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listtargetiqn")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combotargetlun") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listtargetlun")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combonetworknic") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) combobox.set_model(self.builder.get_object("listnetworknic")) combobox.set_active(0) combobox.set_style(style) combobox = self.builder.get_object("combocustomfields") cell = gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 0) combobox.set_model(self.builder.get_object("listcombocustomfields")) combobox.set_active(0) combobox.set_style(style) #print combobox.get_internal_child() # If gtk version is 2.18.0 or higher then add "marks" to scale if hasattr(self.builder.get_object("scalepropvmprio"), "add_mark"): self.builder.get_object("scalepropvmprio").add_mark(0, gtk.POS_BOTTOM, "\nLowest") self.builder.get_object("scalepropvmprio").add_mark(1, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(2, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(3, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(4, gtk.POS_BOTTOM, "\nNormal") self.builder.get_object("scalepropvmprio").add_mark(5, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(6, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(7, gtk.POS_BOTTOM, "") self.builder.get_object("scalepropvmprio").add_mark(8, gtk.POS_BOTTOM, "\nHighest") # Manual function to set the default buttons on dialogs/window # Default buttons could be pressed with enter without need do click self.set_window_defaults() # If we need a master password for connect to servers without password: # Show the dialog asking master password if str(self.config["gui"]["save_password"]) == "True": self.builder.get_object("masterpassword").show() self.windowmap = MyDotWindow(self.builder.get_object("viewportmap"), self.treestore, self.treeview) def adjust_scrollbar_performance(self): for widget in ["scrolledwindow47", "scrolledwindow48", "scrolledwindow49", "scrolledwindow50"]: self.builder.get_object(widget).grab_focus() adj = self.builder.get_object(widget).get_hadjustment() adj.set_value(adj.upper - adj.page_size) def func_cell_data_treesearch(self, column, cell, model, iter_ref, user_data): # Test function don't used print column, cell, model, iter_ref, user_data def set_window_defaults(self): """ Function to define what button is the default for each window/dialog Default button could be pressed with enter key """ widgets = ["addserverpassword","addserverusername","snaptplname", "snapshotname", "vmaddnewdisk_name", "txtcopyvmname", "txtpropvmname", "txtnetworkname", "txtmasterpassword", "txtaskmasterpassword" ] dialogs = { "addserver" : "connectAddServer", "newvmdisk" : "acceptnewvmdisk", "vmaddnewdisk" : "acceptvmaddnewdisk", "dialogsnapshotname" : "btacceptsnapshotname", "dialogsnaptplname" : "btacceptsnaptplname", "dialogsnapshotdelete" : "btacceptsnapshotdelete", "vmattachdisk": "btacceptattachdisk", "dialogdeletevm" : "dialogdelete_accept", "dialogdeletevdi" : "dialogdeletevdi_accept", "windowcopyvm" : "windowcopyvm_copy", "dialogvmprop" : "btvmpropaccept", "dialogdeletehostnetwork" : "acceptdialogdeletehostnetwork", "dialogdeletehostnic" : "acceptdialogdeletehostnic", "addbond" : "btacceptaddbond", "newnetwork" : "acceptnewnetwork", "dialogoptions" : "acceptdialogoptions", "masterpassword" : "acceptmasterpassword", "dialogeditnetwork" : "accepteditnetwork", "dialognetworkrestart" : "acceptdialognetworkrestart", "vmimport" : "nextvmimport", "mgmtinterface" : "acceptmgmtinterface", "newpool" : "acceptnewpool" } # For each dialog for wid in dialogs: # Set the flag indicating the widget could be a default button self.builder.get_object(dialogs[wid]).set_flags(gtk.CAN_DEFAULT) # If widget is a dialog if type(self.builder.get_object(wid)) == type(gtk.Dialog()): # Set the button with "id response = 1" as default self.builder.get_object(wid).set_default_response(1) else: # If is a Gtk.Window set the indicated button as default self.builder.get_object(wid).set_default(self.builder.get_object(dialogs[wid])) for wid in widgets: # For each button indicate that it may be the default button self.builder.get_object(wid).set_activates_default(True) def visible_func_templates(self, model, iter_ref, user_data=None): name = self.builder.get_object("listtemplates").get_value(iter_ref, 1) txttemplatesearch = self.builder.get_object("txttemplatesearch") if txttemplatesearch.get_text().strip() == "": return True else: return name.lower().count(txttemplatesearch.get_text().lower()) > 0 def visible_func(self, model, iter_ref, user_data=None): """ This function define if a element should be showed or not in left tree This function checks configuration values and show/hide elements Returning False you hide the element, returning True you show the element """ host = self.treestore.get_value(iter_ref, 5) ref = self.treestore.get_value(iter_ref, 6) seltype = self.treestore.get_value(iter_ref, 3) if len(self.txttreefilter.get_text())>0 and \ ((seltype == "vm" or seltype == "template" or seltype == "storage" or seltype == "custom_template") and self.treestore.get_value(iter_ref, 1).lower().count(self.txttreefilter.get_text().lower()) == 0): return False if seltype == "vm" and str(self.config["gui"]["show_hidden_vms"]) == "False" and host and ref and \ self.xc_servers[host].all_vms[ref].get("other_config") and \ str(self.xc_servers[host].all_vms[ref]["other_config"].get("HideFromXenCenter")).lower() == "true": return False if seltype == "template": if self.config["gui"]["show_xs_templates"] == "False" or \ not self.config["gui"]["show_xs_templates"]: return False elif seltype == "custom_template": if self.config["gui"]["show_custom_templates"] == "False" or \ not self.config["gui"]["show_custom_templates"]: return False elif seltype == "storage": if self.config["gui"]["show_local_storage"] == "False" or \ not self.config["gui"]["show_local_storage"]: if host and ref: if not self.xc_servers[host].all_storage[ref]['shared']: return False return True def foreach_connect(self, model, path, iter_ref, user_data): """ This function connect or disconnect depends user_data value if user_data is True then connect all disconnected servers if user_data is False then disconnect all connected servers No code commented because doesn't work so well.. """ if self.treestore.get_value(iter_ref, 3) == "server": if self.treestore.get_value(iter_ref, 4) == "Disconnected": if user_data: name = self.treestore.get_value(iter_ref, 1) if self.config_hosts[name][1]: path = self.modelfilter.convert_path_to_child_path(path) self.treeview.get_selection().select_path(path) iter_ref= self.treestore.get_iter(path) self.selected_iter = iter_ref self.selected_name = self.treestore.get_value(iter_ref, 1) self.selected_uuid = self.treestore.get_value(iter_ref, 2) self.selected_type = self.treestore.get_value(iter_ref, 3) self.selected_state = self.treestore.get_value(iter_ref, 4) self.selected_host = self.treestore.get_value(iter_ref, 5) self.selected_ip = self.treestore.get_value(iter_ref, 8) self.on_m_connect_activate(self.treestore, None) self.treesearch.expand_all() if self.treestore.get_value(iter_ref, 3) == "host" or self.treestore.get_value(iter_ref, 3) == "pool": if self.treestore.get_value(iter_ref, 4) == "Running": if not user_data: path = self.modelfilter.convert_path_to_child_path(path) self.treeview.get_selection().select_path(path) iter_ref= self.treestore.get_iter(path) self.selected_iter = iter_ref self.selected_name = self.treestore.get_value(iter_ref, 1) self.selected_uuid = self.treestore.get_value(iter_ref, 2) self.selected_type = self.treestore.get_value(iter_ref, 3) self.selected_state = self.treestore.get_value(iter_ref, 4) self.selected_host = self.treestore.get_value(iter_ref, 5) self.selected_ip = self.treestore.get_value(iter_ref, 8) self.on_m_disconnect_activate(self.treestore, None) self.treesearch.expand_all() else: print "**", self.treestore.get_value(iter_ref, 4) def on_window1_configure_event(self, widget, data=None): self.on_window1_size_request(widget, data) def on_window1_size_request(self, widget, data=None): if self.hWnd != 0: console_area = self.builder.get_object("frameconsole") console_area.realize() console_alloc = console_area.get_allocation() window_alloc = self.window.get_position() x = console_alloc.x + window_alloc[0] + 10 y = console_alloc.y + window_alloc[1] + 47 win32gui.MoveWindow(self.hWnd, x, y, console_alloc.width-10, console_alloc.height-5, 1) def on_console_area_key_press_event(self, widget, event): self.tunnel.key = hex(event.hardware_keycode - 8) def on_aboutdialog_close(self, widget, data=None): """ Function to hide about dialog when you close it """ self.builder.get_object("aboutdialog").hide() def on_acceptmasterpassword_clicked(self, widget, data=None): """ Function what checks ff you typed a master password is right """ # Create a md5 object m = hashlib.md5() password = self.builder.get_object("txtaskmasterpassword").get_text() # Add password typed to md5 object m.update(password) # m.hexdigest() is a md5 ascii password (as saved in the configuration) if self.config["gui"]["master_password"] != m.hexdigest(): # If is wrong show the label indicating is a wrong password self.builder.get_object("lblwrongpassword").show() else: # If is a good password set to global variable "password" and hide dialog self.password = password self.builder.get_object("masterpassword").hide() def on_cancelmasterpassword_clicked(self, widget, data=None): """ Function called when you cancel the master password dialog. """ #If you cancel the dialog, then set global variable "password" to None self.password = None self.builder.get_object("masterpassword").hide() def on_txtaskmasterpassword_changed(self, widget, data=None): """ Function called when you write or remove characters on master password entry """ # If you check "save server passwords" then you need specify a master password # If len of master password is 0, then disable "Accept" button in options dialog self.builder.get_object("acceptmasterpassword").set_sensitive(len(widget.get_text())) def update_tabs(self): """ Function called when you select a element from left tree Depending selected type show or hide differents tabs """ frames = ("framestggeneral", "framememory","framestgdisks","framevmgeneral", "framevmstorage", "framevmnetwork", "framehostgeneral", "framehostnetwork", "framehoststorage", "frameconsole", "framehostnics", "framesnapshots","frameperformance","frametplgeneral","framehome","frameconsole", "framepoolgeneral", "framelogs", "framesearch", "frameusers", "framemaps", "framehosthw") showframes = { "pool" : ["framepoolgeneral", "framelogs", "framesearch", "framemaps"], "home" : ["framehome"], "vm" : ["framevmgeneral", "framememory", "framevmstorage", "framevmnetwork", "framelogs", "framesnapshots","frameperformance"], "host" : ["framesearch","framehostgeneral", "framehostnetwork", "framehoststorage", "framelogs", "frameconsole", "framehostnics", "frameperformance", "frameusers", "framemaps"], "template" : ["frametplgeneral","framevmnetwork","framehostgeneral"], "custom_template" : ["frametplgeneral","framevmnetwork", "framevmstorage", "framelogs"], "storage" : ["framestggeneral","framestgdisks", "framelogs"], } if self.selected_type in showframes: [self.builder.get_object(frame).show() for frame in showframes[self.selected_type]] [self.builder.get_object(frame).hide() for frame in frames if frame not in showframes[self.selected_type]] if self.selected_type == "pool": self.xc_servers[self.selected_host].update_tab_pool_general(self.selected_ref, self.builder) elif self.selected_type == "vm": # If "VM" is running, show console tab, else hide if self.selected_state == "Running": self.builder.get_object("frameconsole").show() else: self.builder.get_object("frameconsole").hide() self.xc_servers[self.selected_host].update_tab_vm_general(self.selected_ref, self.builder) elif self.selected_type == "host": self.xc_servers[self.selected_host].update_tab_host_general(self.selected_ref, self.builder) if self.xc_servers[self.selected_host].has_hardware_script(self.selected_ref): self.builder.get_object("framehosthw").show() else: self.builder.get_object("framehosthw").hide() elif self.selected_type == "template": self.xc_servers[self.selected_host].update_tab_template(self.selected_ref, self.builder) elif self.selected_type == "custom_template": self.xc_servers[self.selected_host].update_tab_template(self.selected_ref, self.builder) elif self.selected_type == "storage": operations = self.xc_servers[self.selected_host].all_storage[self.selected_ref]['allowed_operations'] if operations.count("vdi_create"): self.builder.get_object("btstgnewdisk").show() else: self.builder.get_object("btstgnewdisk").hide() self.xc_servers[self.selected_host].update_tab_storage(self.selected_ref, self.builder) # Experimental only try: import webkit import glob for deletepage in self.delete_pages: # FIXME: remove doesn't work self.builder.get_object("tabbox").get_nth_page(deletepage).hide_all() self.delete_pages = [] for infile in glob.glob("plugins/*.xml"): data = open(infile).read() """ dom = xml.dom.minidom.parseString(data) nodes = dom.getElementsByTagName("XenCenterPlugin") applicable = False if len(nodes[0].getElementsByTagName("TabPage")): for tabpage in nodes[0].getElementsByTagName("TabPage"): if tabpage.attributes.getNamedItem("search"): search_uuid = tabpage.attributes.getNamedItem("search").value tabname = tabpage.attributes.getNamedItem("name").value # REVISE url = tabpage.attributes.getNamedItem("url").value # REVISE if len(nodes[0].getElementsByTagName("Search")): host = self.selected_host [applicable, ip] = self.plugin_get_search(nodes, search_uuid, host, ref) """ host = self.selected_host ref = self.selected_ref [applicable, ip, url, tabname] = self.process_xml(data, host, ref) if applicable: view = webkit.WebView() browser = gtk.ScrolledWindow() url = url.replace("{$ip_address}", ip) view.open(url) browser.add_with_viewport(view) tablabel = gtk.Label(tabname) self.delete_pages.append( \ self.builder.get_object("tabbox").append_page(\ browser, tablabel)) browser.show_all() except: pass def process_xml(self, data, host, ref): dom = xml.dom.minidom.parseString(data) if dom.documentElement.nodeName != u'XenCenterPlugin': print "no XenCenterPlugin" return node = dom.documentElement ip = None applicable = False for tabpage in node.getElementsByTagName("TabPage"): search_uuid = tabpage.getAttribute('search') tabname = tabpage.getAttribute("name") # REVISE url = tabpage.getAttribute("url") # REVISE if search_uuid and tabname and url: for search in [e for e in node.getElementsByTagName("Search") if e.getAttribute("uuid") == search_uuid]: for query in search.getElementsByTagName("Query"): for queryscope in [e for e in query.getElementsByTagName("QueryScope")[0].childNodes if e.nodeType != dom.TEXT_NODE]: if queryscope.nodeName == "LocalSR": if self.selected_type == "storage": shared = self.xc_servers[self.selected_host].all_storage[self.selected_ref]['shared'] if not shared: applicable = True elif queryscope.nodeName == "RemoteSR": if self.selected_type == "storage": shared = self.xc_servers[self.selected_host].all_storage[self.selected_ref]['shared'] if shared: applicable = True elif queryscope.nodeName == "Pool": # REVISE if self.selected_type == "pool": applicable = True elif queryscope.nodeName == "Vm": # REVISE if self.selected_type == "vm": applicable = True elif queryscope.nodeName == "Host": # REVISE if self.selected_type == "host": applicable = True if applicable: for enumpropertyquery in query.getElementsByTagName("EnumPropertyQuery"): data = None if self.selected_type == "storage": data = self.xc_servers[host].all_storage[ref] pbds = data['PBDs'] ip = "" if "target" in self.xc_servers[host].all_pbd[pbds[0]]["device_config"]: ip = self.xc_servers[host].all_pbd[pbds[0]]["device_config"]['target'] #ip = data["name_description"].split(" ")[2][1:] elif self.selected_type == "vm": data = self.xc_servers[host].all_vms[ref] ip = self.selected_ip if self.selected_type == "host": data = self.xc_servers[host].all_hosts[ref] ip = self.selected_ip if self.selected_type == "pool": data = self.xc_servers[host].all_pools[ref] ip = self.selected_ip if data: prop = enumpropertyquery.attributes.getNamedItem("property").value equals = enumpropertyquery.attributes.getNamedItem("equals").value value = enumpropertyquery.attributes.getNamedItem("query").value if prop in data: if equals == "no": if isinstance(data[prop], str): applicable = data[prop].count(value)>0 else: # REVISE applicable = False else: applicable = (data == value) else: if "XenCenter.CustomFields." + prop in data["other_config"]: applicable = True url = url.replace("{$%s}" % prop, data["other_config"]["XenCenter.CustomFields." + prop]) else: applicable = False return [applicable, ip, url, tabname] def plugin_get_search(self, nodes, search_uuid, host, ref): """ Determine if plugin is applicable """ applicable = False ip = None for search in nodes[0].getElementsByTagName("Search"): if search.attributes.getNamedItem("uuid").value == search_uuid: for query in search.getElementsByTagName("Query"): queryscopes = query.getElementsByTagName("QueryScope") for queryscope in queryscopes[0].childNodes: if queryscope.nodeName != "#text": if queryscope.nodeName == "LocalSR": if self.selected_type == "storage": shared = self.xc_servers[host].all_storage[ref]['shared'] if not shared: applicable = True elif queryscope.nodeName == "RemoteSR": if self.selected_type == "storage": shared = self.xc_servers[host].all_storage[ref]['shared'] if shared: applicable = True elif queryscope.nodeName == "Pool": # REVISE if self.selected_type == "pool": applicable = True elif queryscope.nodeName == "Vm": # REVISE if self.selected_type == "VM": applicable = True elif queryscope.nodeName == "Host": # REVISE if self.selected_type == "host": applicable = True if applicable: for enumpropertyquery in query.getElementsByTagName("EnumPropertyQuery"): data = None if self.selected_type == "storage": data = self.xc_servers[host].all_storage[ref] ip = data["name_description"].split(" ")[2][1:] elif self.selected_type == "vm": data = self.xc_servers[host].all_vms[ref] ip = self.selected_ip if self.selected_type == "host": data = self.xc_servers[host].all_hosts[ref] ip = self.selected_ip if self.selected_type == "pool": data = self.xc_servers[host].all_pools[ref] ip = self.selected_ip if data: prop = enumpropertyquery.attributes.getNamedItem("property").value equals = enumpropertyquery.attributes.getNamedItem("equals").value value = enumpropertyquery.attributes.getNamedItem("query").value if prop in data: if equals == "no": if isinstance(data[prop], str): applicable = data[prop].count(value)>0 else: # REVISE applicable = False else: applicable = (data == value) else: applicable = False return [applicable, ip] def on_window_destroy(self, widget, data=None): """ Function called when you close the window or press Quit """ # For each server if self.tunnel: self.tunnel.close() for sh in self.xc_servers: # Stop the threads setting True the condition variables self.xc_servers[sh].halt = True self.xc_servers[sh].halt_search = True self.xc_servers[sh].halt_import = True self.xc_servers[sh].halt_performance = True # Do a logout, remember logout disconnect to server and unregister events self.xc_servers[sh].logout() # For windows only: close the tightvnc console if self.hWnd != 0: win32gui.PostMessage(self.hWnd, win32con.WM_QUIT, 0, 0) self.hWnd = 0 # Save unsaved changes self.config.write() # Exit! gtk.main_quit() if self.tunnel: self.tunnel.close() def count_list(self, model, path, iter_ref, user_data): """ Function to count elements from list.. """ #TODO: remove and use __len__() self.nelements = self.nelements + 1 def on_tabbox_focus_tab(self, widget, data=None, data2=None): """ Function called when you click on a tab Tabbox contains all possible tabs, when you click on a tab first we will check the name Depending of this name we will do differents actions """ # Get the Tab name #tab_label = widget.get_tab_label(widget.get_nth_page(data2)).name tab_label = gtk.Buildable.get_name(widget.get_tab_label(widget.get_nth_page(data2))) # Set as selected self.selected_tab = tab_label if tab_label != "VM_Console": if self.tunnel and not self.noclosevnc: self.tunnel.close() # If vnc console was opened and we change to another, close it self.builder.get_object("menuitem_tools_cad").set_sensitive(False) if hasattr(self,"vnc") and self.vnc and not self.noclosevnc: self.vnc.destroy() self.builder.get_object("windowvncundock").hide() self.vnc = None # Same on Windows if self.hWnd != 0: win32gui.PostMessage(self.hWnd, win32con.WM_QUIT, 0, 0) self.hWnd = 0 if tab_label != "HOST_Search" and self.selected_host: # If we change tab to another different to HOST Search, then stop the filling thread self.xc_servers[self.selected_host].halt_search = True if tab_label != "VM_Performance" and self.selected_host: self.xc_servers[self.selected_host].halt_performance = True if tab_label == "VM_Console": self.builder.get_object("menuitem_tools_cad").set_sensitive(True) self.treeview = self.builder.get_object("treevm") if hasattr(self,"vnc") and self.vnc: if self.tunnel: self.tunnel.close() self.vnc.destroy() self.builder.get_object("windowvncundock").hide() self.vnc = None if self.treeview.get_cursor()[1]: state = self.selected_state host = self.selected_host # First checks if VM is running self.builder.get_object("btenterfullscreen").grab_focus() self.builder.get_object("console_area").grab_focus() if state == "Running": if sys.platform != "win32" and sys.platform != "darwin": # Create a gtkvnc object self.vnc = gtkvnc.Display() # Add to gtkvnc to a console area console_area = self.builder.get_object("console_area") console_area.add(self.vnc) console_area.show_all() self.vnc.activate() self.vnc.grab_focus() self.vnc.set_pointer_grab(False) self.vnc.set_pointer_local(False) self.vnc.set_keyboard_grab(True) self.vnc.set_shared_flag(True) self.vnc.connect("vnc-disconnected", self.vnc_disconnected) self.vnc.connect("key_press_event", self.on_console_area_key_press_event) from tunnel import Tunnel if self.selected_type == "host": ref = self.xc_servers[self.selected_host].host_vm[self.selected_ref][0] else: ref = self.selected_ref if self.xc_servers[self.selected_host].all_vms[ref]["consoles"]: console_ref = self.xc_servers[self.selected_host].all_vms[ref]["consoles"][0] location = self.xc_servers[self.selected_host].all_console[console_ref]["location"] self.tunnel = Tunnel(self.xc_servers[self.selected_host].session_uuid, location) port = self.tunnel.get_free_port() Thread(target=self.tunnel.listen, args=(port,)).start() time.sleep(1) # And open the connection try: self.vnc.set_depth(1) except: pass self.vnc.connect("vnc-server-cut-text", self.vnc_button_release) self.vnc.open_host("localhost", str(port)) else: print "No console available" elif sys.platform == "darwin": from tunnel import Tunnel if self.selected_type == "host": ref = self.xc_servers[self.selected_host].host_vm[self.selected_ref][0] else: ref = self.selected_ref # Run ./vncviewer with host, vm renf and session ref if self.xc_servers[self.selected_host].all_vms[ref]["consoles"]: console_ref = self.xc_servers[self.selected_host].all_vms[ref]["consoles"][0] location = self.xc_servers[self.selected_host].all_console[console_ref]["location"] self.tunnel = Tunnel(self.xc_servers[self.selected_host].session_uuid, location) port = self.tunnel.get_free_port() Thread(target=self.tunnel.listen, args=(port,)).start() time.sleep(1) os.spawnl(os.P_NOWAIT,"./vncviewer", "vncviewer", "localhost::%s" % port) console_area = self.builder.get_object("console_area") console_alloc = console_area.get_allocation() else: print "No console available" else: if self.selected_type == "host": ref = self.xc_servers[self.selected_host].host_vm[self.selected_ref][0] else: ref = self.selected_ref # Run vncviewer.exe with host, vm renf and session ref param = self.xc_servers[host].get_connect_parameters(ref, self.selected_ip) os.spawnl(os.P_NOWAIT,"vncviewer.exe", "vncviewer.exe", "%s" % (param)) console_area = self.builder.get_object("frameconsole") console_area.realize() console_alloc = console_area.get_allocation() window_alloc = self.window.get_position() x = console_alloc.x + window_alloc[0] + 10 y = console_alloc.y + window_alloc[1] + 47 # On windows we'll move the window.. while win32gui.FindWindow(None, "HVMXEN-%s" % (self.selected_uuid)) == 0 \ and win32gui.FindWindow(None, "XenServer Virtual Terminal") == 0: pass self.hWnd = win32gui.FindWindow(None, "HVMXEN-%s" % (self.selected_uuid)) if self.hWnd == 0: self.hWnd = win32gui.FindWindow(None, "XenServer Virtual Terminal") #win32gui.ShowWindow(self.hWnd, win32con.SW_HIDE) win32gui.MoveWindow(self.hWnd, x, y, console_alloc.width-10, console_alloc.height-5, 1) #win32gui.ShowWindow(self.hWnd, win32con.SW_SHOW) else: print state if tab_label == "VM_Memory": self.update_memory_tab() if tab_label == "VM_Storage": if self.treeview.get_cursor()[1]: # liststorage contains the storage on VM liststorage = self.builder.get_object("listvmstorage") # liststoragdvd contains the possibles dvd/isos to mount on VM liststoragedvd = self.builder.get_object("listvmstoragedvd") #liststoragedvd.set_sort_func(1, self.compare_data) host = self.selected_host # Fill liststorage self.xc_servers[host].fill_vm_storage(self.selected_ref, \ liststorage) # Fill liststoragedvd, fill_vm_storage_dvd return the current dvd/iso mounted active = self.xc_servers[host].fill_vm_storage_dvd(self.selected_ref, \ liststoragedvd) # Flag variable to no emit signal self.set_active = True # Set as the active dvd/iso mounted self.builder.get_object("combovmstoragedvd").set_active(active) self.set_active = False elif tab_label == "VM_Network": if self.treeview.get_cursor()[1]: treenetwork = self.builder.get_object("treevmnetwork") # listvmnetwork contains the networks of a vm listnetwork = self.builder.get_object("listvmnetwork") host = self.selected_host # Fill the list of networks self.xc_servers[host].fill_vm_network(self.selected_ref, \ treenetwork, listnetwork) elif tab_label == "VM_Snapshots": if self.treeview.get_cursor()[1]: treevmsnapshots = self.builder.get_object("treevmsnapshots") # listvmsnapshots contains the snapshots of a vm listvmsnapshots = self.builder.get_object("listvmsnapshots") host = self.selected_host # Fill the list of snapshots self.xc_servers[host].fill_vm_snapshots(self.selected_ref, \ treevmsnapshots, listvmsnapshots) elif tab_label == "VM_Performance": if self.treeview.get_cursor()[1]: # Thread to update performance images ref = self.selected_ref host = self.selected_host if self.selected_type == "vm": self.builder.get_object("scrolledwindow50").show() self.builder.get_object("labeldiskusage").show() Thread(target=self.xc_servers[host].update_performance, args=(self.selected_uuid, ref, \ self.selected_ip, False)).start() else: self.builder.get_object("scrolledwindow50").hide() self.builder.get_object("labeldiskusage").hide() if self.selected_host and self.selected_ref in self.xc_servers[self.selected_host].host_vm: uuid = self.xc_servers[self.selected_host].host_vm[self.selected_ref][1] Thread(target=self.xc_servers[host].update_performance, args=(uuid, ref, \ self.selected_ip, True)).start() elif tab_label == "VM_Logs": if self.treeview.get_cursor()[1]: treeviewlog = self.builder.get_object("treeviewlog") # listlog contains the snapshots of a vm/host listlog = self.builder.get_object("listlog") host = self.selected_host # Fill the list of logs if self.selected_type == "vm": self.xc_servers[host].fill_vm_log(self.selected_uuid, \ treeviewlog, listlog) else: self.xc_servers[host].fill_vm_log(self.selected_uuid, \ treeviewlog, listlog) elif tab_label == "HOST_Users": if self.selected_type == "pool": name = self.xc_servers[self.selected_host].all_pools[self.selected_ref]['name_label'] externalauth = self.xc_servers[self.selected_host].get_external_auth(self.xc_servers[self.selected_host]['master']) else: name = self.xc_servers[self.selected_host].all_hosts[self.selected_ref]['name_label'] externalauth = self.xc_servers[self.selected_host].get_external_auth(self.selected_ref) listusers = self.builder.get_object("listusers") self.xc_servers[self.selected_host].fill_domain_users(self.selected_ref, listusers) if externalauth[0] == "": self.builder.get_object("btjoindomain").set_sensitive(True) self.builder.get_object("btleavedomain").set_sensitive(False) self.builder.get_object("lblusersdomain").set_text("AD is not currently configured for '" + self.selected_name + "'. To enable AD authentication, click Join.") else: self.builder.get_object("btleavedomain").set_sensitive(True) self.builder.get_object("btjoindomain").set_sensitive(False) self.builder.get_object("lblusersdomain").set_text("Pool/host " + self.selected_name + " belongs to domain '" + externalauth[1] + "'. To enable AD authentication, click Join.") elif tab_label == "HOST_Storage": if self.treeview.get_cursor()[1]: # listhoststorage contains the snapshots of a vm/host liststorage = self.builder.get_object("listhoststorage") host = self.selected_host # Fill the list of storage self.xc_servers[host].fill_host_storage(self.selected_ref, \ liststorage) elif tab_label == "HOST_Nics": if self.treeview.get_cursor()[1]: """ liststorage = self.builder.get_object("listhostnics") host = self.selected_host self.xc_servers[host].fill_host_nics(self.selected_ref, \ liststorage) """ # Call to update_tab_host_nics to fill the host nics self.update_tab_host_nics() elif tab_label == "HOST_Search": if self.treeview.get_cursor()[1]: host = self.selected_host self.xc_servers[host].halt_search = False # Host_Search contains a live monitoring status of VM # Create a thread to fill "listsearch" self.xc_servers[host].thread_host_search(self.selected_ref, self.listsearch) # Expand "treesearch" self.treesearch.expand_all() elif tab_label == "HOST_Hardware": if self.selected_host: self.xc_servers[self.selected_host].fill_host_hardware(self.selected_ref) elif tab_label == "HOST_Network": # Call to update_tab_host_network to fill the host networks self.update_tab_host_network() elif tab_label == "Local_Storage": if self.treeview.get_cursor()[1]: # liststg contains the vdi under storage liststg = self.builder.get_object("liststg") liststg.set_sort_func(1, self.compare_data) liststg.set_sort_column_id(1, gtk.SORT_ASCENDING) host = self.selected_host # Fill the list of storage if host: self.xc_servers[host].fill_local_storage(self.selected_ref, \ liststg) elif tab_label == "Maps": self.update_maps() def compare_data(self, model, iter1, iter2): data1 = model.get_value(iter1,1) data2 = model.get_value(iter2,1) return cmp(data1, data2) def update_maps(self): dotcode = """ digraph G { overlap=false; bgcolor=white; node [shape=polygon, sides=6, fontname="Verdana", fontsize="8"]; edge [color=deepskyblue3, fontname="Verdana", fontsize="5"]; """ if self.selected_host: show_halted_vms = self.builder.get_object("check_show_halted_vms").get_active() if self.builder.get_object("check_show_network").get_active(): relation = self.xc_servers[self.selected_host].get_network_relation(self.selected_ref, show_halted_vms) for network in relation: uuid, name = network.split("_", 1) safename = name.replace("&","&").replace("<", "<").replace("\"", """) if self.builder.get_object("check_unused_network").get_active() or relation[network]: dotcode += '"%s"[shape=plaintext, label=<
%s
> tooltip="%s"];' % (uuid, safename, name) dotcode += "\n" for vm in relation[network]: uuid2, name2 = vm.split("_", 1) dotcode += '"%s"[shape=plaintext, label=<
%s
>URL="%s" tooltip="%s"];' % (uuid2, name2, uuid2, name2) dotcode += "\n" dotcode += '"%s" -> "%s"' % (uuid, uuid2) dotcode += "\n" if self.builder.get_object("check_show_storage").get_active(): dotcode += 'edge [color=forestgreen, fontname="Verdana", fontsize="5"];' relation = self.xc_servers[self.selected_host].get_storage_relation(self.selected_ref, show_halted_vms) for storage in relation: uuid, name = storage.split("_", 1) safename = name.replace("&","&").replace("<", "<").replace("\"", """) if self.builder.get_object("check_unused_storage").get_active() or relation[storage]: dotcode += '"%s"[shape=plaintext, label=<
%s
>URL="%s" tooltip="%s"];' % (uuid, safename, uuid, name) dotcode += "\n" for vm in relation[storage]: uuid2, name2 = vm.split("_", 1) safename2 = name2.replace("&","&").replace("<", "<").replace("\"", """) dotcode += '"%s"[shape=plaintext, label=<
%s
>URL="%s" tooltip="%s"];' % (uuid2, safename2, uuid2, name2) dotcode += "\n" dotcode += '"%s" -> "%s"' % (uuid2, uuid) dotcode += "\n" dotcode += "}" self.windowmap.set_dotcode(dotcode) self.builder.get_object("viewportmap").show_all() def on_btopenfile_activate(self, widget, data=None): """ Obsoleted function """ filechooser = self.fileopen.get_children()[0].get_children()[0] if filechooser.get_filename(): self.xc_servers[self.selected_host].import_vm(self.selected_ref, filechooser.get_filename()) self.fileopen.hide() else: self.show_error_dlg("Select a file") def on_btsavefile_activate(self, widget, data=None): """ Function called when you press "Export VM" """ filechooser = self.filesave.get_children()[0].get_children()[0] if filechooser.get_filename(): # Call to export_vm function with vm renf and filename choosed if self.export_snap: print "Export snap.." self.xc_servers[self.selected_host].export_vm(self.selected_snap_ref, filechooser.get_filename(), self.selected_ref) self.export_snap = False elif self.export_snap_vm: print "Export snap as VM.." self.xc_servers[self.selected_host].export_vm(self.selected_snap_ref, filechooser.get_filename(), self.selected_ref, as_vm = True) self.export_snap_vm = False else: self.xc_servers[self.selected_host].export_vm(self.selected_ref, filechooser.get_filename()) self.filesave.hide() self.builder.get_object("tabbox").set_current_page(17) else: self.show_error_dlg("Select a file") def on_filesave_confirm_overwrite(self, widget, data=None): """ Not used function """ print widget print data def on_btcancelsavefile_activate(self, widget, data=None): """ If you press cancel on "Export VM" dialog, then close the dialog """ self.export_snap = False self.filesave.hide() def on_btcancelopenfile_activate(self, widget, data=None): """ Not used function """ self.fileopen.hide() def on_treevm_button_press_event(self, widget, event): """ Function is called when you do click (or double click) on left tree """ x = int(event.x) y = int(event.y) time = event.time if x == -10 and y == -10: pthinfo = [self.modelfilter.get_path(self.treeview.get_selection().get_selected()[1]), None, 0, 0] else: pthinfo = widget.get_path_at_pos(x, y) if event.type == gtk.gdk._2BUTTON_PRESS: # On double click, if server is disconnected then connect to it if self.selected_state == "Disconnected": self.on_m_connect_activate(widget, None) elif pthinfo is not None: # On single click path, col, cellx, celly = pthinfo widget.grab_focus() widget.set_cursor( path, col, 0) path = self.modelfilter.convert_path_to_child_path(path) iter_ref= self.treestore.get_iter(path) # Define selected variables self.selected_iter = iter_ref self.selected_name = self.treestore.get_value(iter_ref, 1) self.selected_uuid = self.treestore.get_value(iter_ref, 2) self.selected_type = self.treestore.get_value(iter_ref, 3) self.selected_state = self.treestore.get_value(iter_ref, 4) self.selected_host = self.treestore.get_value(iter_ref, 5) self.selected_ip = self.treestore.get_value(iter_ref, 8) # Used to prevent not manual changes previous_ref = self.selected_ref self.selected_ref = self.treestore.get_value(iter_ref, 6) # Define the possible actions for VM/host/storage.. if self.selected_type == "vm": self.selected_actions = self.xc_servers[self.selected_host].get_actions(self.selected_ref) else: self.selected_actions = self.treestore.get_value(iter_ref, 7) #if type(self.selected_actions) == type(""): # self.selected_actions = eval(self.selected_actions) # Update menubar and tabs with new selection self.update_menubar() self.update_tabs() if self.selected_ref != previous_ref: # If you selected a different element than previous # then select the correct tab for selected type if self.selected_type == "vm": self.builder.get_object("tabbox").set_current_page(5) else: self.builder.get_object("tabbox").set_current_page(3) if self.selected_type == "pool": self.builder.get_object("tabbox").set_current_page(0) elif self.selected_type == "host": self.builder.get_object("tabbox").set_current_page(1) self.builder.get_object("tabbox").set_current_page(4) elif self.selected_type == "server": self.builder.get_object("tabbox").set_current_page(2) elif self.selected_type == "template": self.builder.get_object("tabbox").set_current_page(2) elif self.selected_type == "custom_template": self.builder.get_object("tabbox").set_current_page(2) elif self.selected_type == "storage": self.builder.get_object("tabbox").set_current_page(1) if event.button == 3: # On right click.. # Show the menu menu_vm = self.builder.get_object("menu_vm") collapsed = False expanded = False can_expand_or_collapse = False for child in range(0,self.treestore.iter_n_children(self.selected_iter)): iter_ref= self.treestore.iter_nth_child(self.selected_iter, child) if self.treestore.iter_n_children(iter_ref): can_expand_or_collapse = True path = self.treestore.get_path(iter_ref) if self.treeview.row_expanded(path): expanded = True else: collapsed = True if can_expand_or_collapse: if collapsed: self.builder.get_object("expandall").show() else: self.builder.get_object("expandall").hide() if expanded: self.builder.get_object("collapsechildren").show() else: self.builder.get_object("collapsechildren").hide() else: self.builder.get_object("expandall").hide() self.builder.get_object("collapsechildren").hide() for child in menu_vm.get_children(): # Menuitems are with name "m_action" # Checks if "action" is on selected_actions" typestg = None pbdstg = 1 broken = False if self.selected_type == "storage": typestg = self.xc_servers[self.selected_host].all_storage[self.selected_ref]["type"] pbdstg = len(self.xc_servers[self.selected_host].all_storage[self.selected_ref]["PBDs"]) if gtk.Buildable.get_name(child)[0:2] == "m_": if not self.selected_actions or \ self.selected_actions.count(gtk.Buildable.get_name(child)[2:]) \ == 0: child.hide() else: # If selected_type is storage and typestg is not "lvm" or "udev" if typestg != "lvm" and typestg != "udev": # If has not pbds.. then enable only "Reattach" and "Forget" if pbdstg == 0 and (gtk.Buildable.get_name(child) == "m_plug" or gtk.Buildable.get_name(child) == "m_forget"): child.show() else: # Disable else if pbdstg == 0: child.hide() else: # If has pbds.. disable "Reattach" if gtk.Buildable.get_name(child) != "m_plug": child.show() else: child.hide() else: child.hide() # Properties will be showed always else on home and disconnected servers if gtk.Buildable.get_name(child) == "properties": if self.selected_type == "home": child.hide() elif self.selected_type == "server" and not self.selected_ref: child.hide() else: child.show() # Delete will be showed only on pool elif gtk.Buildable.get_name(child) == "delete": if self.selected_type == "pool": child.show() else: child.hide() # Install XenServer Tools only on elif gtk.Buildable.get_name(child) == "installxenservertools": if self.selected_type == "vm" and self.selected_state == "Running": self.builder.get_object("separator1").show() self.builder.get_object("separator2").show() child.show() else: self.builder.get_object("separator1").hide() self.builder.get_object("separator2").hide() child.hide() # Repair storage, only on broken storage elif gtk.Buildable.get_name(child) == "m_repair_storage": if self.selected_type == "storage": broken = self.xc_servers[self.selected_host].is_storage_broken(self.selected_ref) if broken: child.show() else: child.hide() # Add to pool, only for servers without pools elif gtk.Buildable.get_name(child) == "m_add_to_pool": if self.selected_type == "host": pool_ref = self.xc_servers[self.selected_host].all_pools.keys()[0] if self.xc_servers[self.selected_host].all_pools[pool_ref]["name_label"] == "": child.show() else: child.hide() else: child.hide() # Add server to pool from pool menu elif gtk.Buildable.get_name(child) == "m_pool_add_server": if self.selected_type == "pool": child.show() else: child.hide() menu_vm.popup( None, None, None, event.button, time) # Update toolbar and set label/image on top right pane self.update_toolbar() self.headlabel.set_label(self.selected_name) self.headimage.set_from_pixbuf(self.treestore.get_value(iter_ref, 0)) def vnc_disconnected(self, info): print "VNC disconnected..", info def on_txttreefilter_changed(self, widget, data=None): """ Function called when on left top entry you write text to filter """ self.modelfilter.refilter() self.treeview.expand_all() def show_error_dlg(self, error_string, error_title="Error"): """This Function is used to show an error dialog when an error occurs. error_string - The error string that will be displayed on the dialog. http://www.pygtk.org/articles/extending-our-pygtk-application/extending-our-pygtk-application.htm """ self.builder.get_object("walert").set_title(error_title) self.builder.get_object("walerttext").set_text(error_string) self.builder.get_object("walert").show() def on_closewalert_clicked(self, widget, data=None): self.builder.get_object("walert").hide() def push_alert(self, alert): """ Function to set in statusbar an alert """ self.statusbar.get_children()[0].get_children()[0].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#000000')) self.statusbar.push(1, alert) def push_error_alert(self, alert): """ Function to set in statusbar an error alert """ self.statusbar.get_children()[0].get_children()[0].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FF0000')) self.statusbar.push(1, alert) def not_implemented_yet(self, widget, data=None): """ Some functions are not implemented yet, show the dialog """ self.show_error_dlg("Not implemented yet") def dump(self, obj): """ Internal use only """ for attr in dir(obj): print "obj.%s = %s" % (attr, getattr(obj, attr)) def signal_handler(self): """ Function called when oxc gets a signal """ print "Exiting..." for sh in self.xc_servers: self.xc_servers[sh].halt = True self.xc_servers[sh].halt_search = True self.xc_servers[sh].halt_performance = True self.xc_servers[sh].logout() self.config.write() if self.hWnd != 0: win32gui.PostMessage(self.hWnd, win32con.WM_QUIT, 0, 0) self.hWnd = 0 def on_delete_event(self, widget, event): """ Returning True, the window will not be destroyed """ widget.hide() return True def convert_bytes(self, bytes): """ Convert bytes to string http://www.5dollarwhitebox.org/drupal/node/84 """ bytes = float(bytes) if bytes >= 1099511627776: terabytes = bytes / 1099511627776 size = '%.1fT' % terabytes elif bytes >= 1073741824: gigabytes = bytes / 1073741824 size = '%.1fG' % gigabytes elif bytes >= 1048576: megabytes = bytes / 1048576 size = '%.1fM' % megabytes elif bytes >= 1024: kilobytes = bytes / 1024 size = '%.1fK' % kilobytes else: size = '%.1fb' % bytes return size def convert_bytes_mb(self, n): """ Convert byes to mb string """ n = float(n) K, M = 1 << 10, 1 << 20 if n >= M: return '%d' % (float(n) / M) elif n >= K: return '%d' % (float(n) / K) else: return '%d' % n if __name__ == "__main__": # Main function gobject.threads_init() gtk.gdk.threads_init() wine = oxcWindow() gtk.main() openxenmanager/oxcSERVER.py0000644000175000017500000041007711636446664014406 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # Copyright (C) 2011 Cheng Sun # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk import os import platform import sys, shutil import datetime import xml.dom.minidom import pdb import rrdinfo import time import put import gobject from rrd import RRD, XPORT import xml.sax.saxutils as saxutils import traceback from threading import Thread from configobj import ConfigObj from operator import itemgetter from pygtk_chart import line_chart from messages import messages, messages_header from oxcSERVER_vm import * from oxcSERVER_host import * from oxcSERVER_properties import * from oxcSERVER_storage import * from oxcSERVER_alerts import * from oxcSERVER_addserver import * from oxcSERVER_newvm import * from oxcSERVER_menuitem import * from capabilities import capabilities_text class oxcSERVER(oxcSERVERvm,oxcSERVERhost,oxcSERVERproperties,oxcSERVERstorage,oxcSERVERalerts,oxcSERVERaddserver,oxcSERVERnewvm,oxcSERVERmenuitem): session_uuid = None is_connected = False host_vm = {} set_descriptions = {} halt = False halt_search = False halt_import = False track_tasks = {} tasks = {} vboxchildcancel = {} vboxchildprogressbar = {} vboxchildprogress = {} autostart = {} vif_plug = [] flag_vif_plug = False found_iter = "" import_ref = None import_start = False import_make_into_template = False poolroot = None hostroot = {} last_storage_iter = None pbdcreate = [] def __init__(self, host, user, password, wine, ssl = False, port = 80): super(oxcSERVER, self).__init__() self.host = host self.hostname = host self.wine = wine self.user = user self.password = password self.ssl = ssl self.port = port def logout(self): self.halt_search = True self.halt = True if self.is_connected: self.connection.event.unregister(self.session_uuid, ["*"]) self.connection.session.logout(self.session_uuid) self.is_connected = False def get_network_relation(self, ref, show_halted_vms): # Get network -> VM relation relation = {} for network in self.all_network: network_name = self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network ') vms = [] for vif in self.all_network[network]['VIFs']: vm = self.all_vif[vif]['VM'] if not vms.count(vm + "_" + self.all_vms[vm]['name_label']): if show_halted_vms or self.all_vms[vm]['power_state'] == "Running": vms.append(vm + "_" + self.all_vms[vm]['name_label']) relation[network + "_" + network_name] = vms return relation def get_storage_relation(self, ref, show_halted_vms): # Get network -> VM relation relation = {} for storage in self.all_storage: storage_name = self.all_storage[storage]['name_label'] vms = [] for vdi in self.all_storage[storage]['VDIs']: vbds = self.all_vdi[vdi]['VBDs'] for vbd in vbds: vm = self.all_vbd[vbd]['VM'] if not vms.count(vm + "_" + self.all_vms[vm]['name_label']): if show_halted_vms or self.all_vms[vm]['power_state'] == "Running": vms.append(vm + "_" + self.all_vms[vm]['name_label']) relation[storage+ "_" + storage_name] = vms return relation def prueba(self): print self.session_uuid task_uuid = self.connection.task.create(self.session_uuid, "Restore2", "Restoring2 ") print task_uuid time.sleep(300) print task_uuid return networks = self.connection.network.get_all_records(self.session_uuid)['Value'] for network in networks: print networks[network]['name_label'] vms = [] for vif in networks[network]['VIFs']: vms.append(self.connection.VIF.get_record(self.session_uuid, vif)['Value']['VM']) # Remove duplicates set = {} map(set.__setitem__, vms, []) for vm in set.keys(): print "\t" + self.connection.VM.get_record(self.session_uuid, vm)['Value']['name_label'] storages = self.connection.SR.get_all_records(self.session_uuid)['Value'] for storage in storages: vms = [] print storages[storage]['name_label'] for vdi in storages[storage]['VDIs']: vbds = self.connection.VDI.get_record(self.session_uuid, vdi)['Value']['VBDs'] for vbd in vbds: vms.append(self.connection.VBD.get_record(self.session_uuid, vbd)['Value']['VM']) set = {} map(set.__setitem__, vms, []) for vm in set.keys(): print "\t" + self.connection.VM.get_record(self.session_uuid, vm)['Value']['name_label'] """ print self.connection.Async.pool_patch.apply(self.session_uuid, "OpaqueRef:772a39ac-e9be-1b14-2b20-ded03b95b20b", "OpaqueRef:be650e6f-8e2f-d937-c525-15fd57568bac") result = self.connection.host.get_system_status_capabilities(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a")['Value'] privacy = {"yes": "1", "maybe": "2", "if_customized": "3", "no": "4"} dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("capability") capabilities = {} for node in nodes: attr = node.attributes key, checked, pii, minsize, maxsize, mintime, maxtime = [attr.getNamedItem(k).value for k \ in ["key", "default-checked", "pii", "min-size", "max-size", "min-time", "max-time"]] capabilities[privacy[pii] + "_" + key] = [checked, minsize, maxsize, mintime, maxtime] totalsize = 0 totaltime = 0 for key in sorted(capabilities.keys()): if key.split("_",2)[1] in capabilities_text: print capabilities_text[key.split("_",2)[1]] checked, minsize, maxsize, mintime, maxtime = [value for value in capabilities[key]] if minsize == maxsize: if maxsize != "-1": totalsize += int(maxsize) size = self.convert_bytes(maxsize) elif minsize == "-1": totalsize += int(maxsize) size = "< %s" % self.convert_bytes(maxsize) else: totalsize += int(maxsize) size = "%s-%s" % (self.convert_bytes(minsize), self.convert_bytes(maxsize)) if mintime == maxtime: if maxtime == "-1": time = "Negligible" else: totaltime += int(maxtime) time = maxtime elif mintime == "-1": totaltime += int(maxtime) time= "< %s" % maxtime else: totaltime += int(maxtime) time= "%s-%s" % (mintime, maxtime) print "\tChecked: %s\n\tSize: %s\n\tTime: %s seconds\n" % (checked, size, time) print "Total Size: < %s Total Time: < %d minutes\n" % (self.convert_bytes(totalsize), totaltime/60) print self.connection.VM.get_record(self.session_uuid, "OpaqueRef:dfd4eb56-d44b-8895-328e-83a36a0807ee") print self.connection.VM.set_PV_kernel(self.session_uuid, "OpaqueRef:dfd4eb56-d44b-8895-328e-83a36a0807ee", "asdfasdf") print "https://%s/vncsnapshot?session_id=%s&ref=%s" % (self.host, self.session_uuid, "OpaqueRef:be4332e6-a8c2-eb35-6b4d-58384e1f8463") time.sleep(30) task_uuid = self.connection.task.create(self.session_uuid, "Backup database pool", "Backup database pool") url = "http://" + self.host + '/pool/xmldbdump?session_id=%s&task_id=%s' % (self.session_uuid, task_uuid['Value']) urllib.urlretrieve(url, "/tmp/backup.xml") import httplib, os filename = "/root/openxencenter/prueba.xml" task_uuid = self.connection.task.create(self.session_uuid, "Restore Pool database", "Restoring database pool " + filename) self.track_tasks[task_uuid['Value']] = "Restore.Pool" size=os.path.getsize(filename) conn = httplib.HTTPConnection("192.168.100.2", 80) conn.putrequest('PUT', '/pool/xmldbdump?session_id=%s&task_id=%s&dry_run=true' % (self.session_uuid, task_uuid['Value']), True, True) conn.putheader("Content-length:", str(size)); conn.endheaders() fp=open(filename, 'r') total = 0 while True: leido = fp.read(16384) total += len(leido) if leido: time.sleep(0.1) conn.send(leido) else: break conn.close() fp.close() value = task_uuid["Value"] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] print self.connection.host.get_all_records(self.session_uuid) print self.connection.host.dmesg(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a") res = self.connection.SR.create(self.session_uuid,"OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338d", {"SCSIid" : "3600a0b8000294d50000043454990e472" }, "0", ">Hardware HBA virtual disk storage", "Hardware HBA SR [IBM - /dev/sde]","lvmohba", "", False,{}) if len(res['ErrorDescription']) > 2: print res['ErrorDescription'][2] #Async.SR.probeOpaqueRef:021466a4-68d1-8e4b-c8ee-4d40e7be1d19OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338dSCSIid3600a0b8000294d50000045784b85e36flvmohba all_storage = self.connection.SR.get_all_records(self.session_uuid)['Value'] all_pbd = self.connection.PBD.get_all_records(self.session_uuid)['Value'] result = self.connection.SR.probe(self.session_uuid, "OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338d", {"SCSIid" : "3600a0b8000294d50000045784b85e36f" }, "lvmohba", {})['Value'] dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("UUID") if len(nodes): reattach = True uuid = nodes[0].childNodes[0].data.strip() for storage in all_storage.values(): if storage["uuid"] == uuid: print storage if len(storage['PBDs']): print all_pbd[storage['PBDs'][0]] reattach = False if reattach: return 0 print "Do you want reattach...." else: print "Please first detach...." else: print "Do you want format.." pass res = self.connection.SR.probe(self.session_uuid, "OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338d", {}, "lvmohba", {}) if len(res['ErrorDescription']) > 2: result = res['ErrorDescription'][3] dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("BlockDevice") disks = {} for node in nodes: size = self.convert_bytes(node.getElementsByTagName("size")[0].childNodes[0].data.strip()) serial = node.getElementsByTagName("serial")[0].childNodes[0].data.strip() scsiid = node.getElementsByTagName("SCSIid")[0].childNodes[0].data.strip() adapter = node.getElementsByTagName("adapter")[0].childNodes[0].data.strip() channel = node.getElementsByTagName("channel")[0].childNodes[0].data.strip() id = node.getElementsByTagName("id")[0].childNodes[0].data.strip() lun = node.getElementsByTagName("lun")[0].childNodes[0].data.strip() vendor = node.getElementsByTagName("vendor")[0].childNodes[0].data.strip() if vendor not in disks: disks[vendor] = [] disks[vendor].append("%s %s %s %s:%s:%s:%s" % (size, serial, scsiid, adapter, channel, id, lun)) print disks else: print "No LUNS found" """ #Async.SR.probe # #OpaqueRef:047a0487-db6d-d273-09a3-d2ac68cb0a7c #OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a # # serverpath/home # server192.168.100.4 # options # #nfs # """ all_vms = self.connection.VM.get_all_records(self.session_uuid)['Value'] all_vdi = self.connection.VDI.get_all_records(self.session_uuid)['Value'] all_vbd = self.connection.VBD.get_all_records(self.session_uuid)['Value'] vm = "OpaqueRef:0bbb21d5-4810-2cdc-9b75-c02415de78bb" vm_vbd = "" vm_vdi = "" for vbd in all_vbd.keys(): if all_vbd[vbd]["VM"] == vm and \ all_vbd[vbd]['type'] == "CD": vm_vbd = vbd for vdi in all_vdi: if all_vdi[vdi]['location'] == "XenCenter.iso": vm_vdi = vdi print self.connection.Async.VBD.eject(self.session_uuid, vm_vbd) print self.connection.VBD.insert(self.session_uuid, vm_vbd, vm_vdi) sr = { "serverpath" : "/home", "server" : "192.168.100.4", "options" : "" } value = self.connection.Async.SR.probe(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a", sr, "nfs", {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') print result dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("SRlist") if len(nodes[0].childNodes): for i in range(1,len(nodes[0].childNodes),2): print nodes[0].childNodes[i].childNodes[1].childNodes[0].data.strip() self.connection.task.destroy(self.session_uuid, value) sr = { "port" : "3260", "target" : "192.168.100.4", "SCSIid" : "14945540000000000000000000100000002a00a002100a00c", "targetIQN" : "iqn.2001-04.com.example:storage.disk2.sys1.xyz", } value = self.connection.Async.SR.probe(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a", sr, "lvmoiscsi",{})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] print task self.connection.task.destroy(self.session_uuid, value) sr = { "port" : "3260", "target": "192.168.100.4", } # chapuser # chappassword value = self.connection.Async.SR.create(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a", sr, "0", "__gui__", "SHOULD NEVER BE CREATED","lvmoiscsi","user", True, {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["error_info"][3]: dom = xml.dom.minidom.parseString(task["error_info"][3]) nodes = dom.getElementsByTagName("TGT") index = nodes[0].childNodes[1].childNodes[0].data.strip() ip = nodes[0].childNodes[3].childNodes[0].data.strip() target = nodes[0].childNodes[5].childNodes[0].data.strip() print "%s (%s)" % (target, ip) else: print task["error_info"][2] self.connection.task.destroy(self.session_uuid, value) sr["targetIQN"] = target value = self.connection.Async.SR.create(self.session_uuid, "OpaqueRef:480dede4-930b-1b5c-54a8-73d38fa56d3a", sr, "0", "__gui__", "SHOULD NEVER BE CREATED","lvmoiscsi","user", True, {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] print task["error_info"][3] if task["error_info"][3]: dom = xml.dom.minidom.parseString(task["error_info"][3]) nodes = dom.getElementsByTagName("LUN") vendor = nodes[0].childNodes[1].childNodes[0].data.strip() lunid = nodes[0].childNodes[3].childNodes[0].data.strip() size = nodes[0].childNodes[5].childNodes[0].data.strip() scsiid = nodes[0].childNodes[7].childNodes[0].data.strip() print "LUN %s: %s (%s)" % (lunid, self.convert_bytes(size), vendor) else: print task["error_info"][2] self.connection.task.destroy(self.session_uuid, value) """ def export_vm(self, uuid): vm_uuid = self.connection.VM.get_by_uuid(self.session_uuid, uuid)['Value'] print "GET /export?ref=%s&session_id=%s HTTP/1.1\r\n\r\n" % (vm_uuid,self.session_uuid) def get_seconds(self, toconvert): converted = datetime.datetime.strptime(str(toconvert), "%Y%m%dT%H:%M:%SZ") totime = time.mktime(converted.timetuple()) #FIXME return totime def format_date(self, toconvert): converted = datetime.datetime.strptime(str(toconvert), "%Y%m%dT%H:%M:%SZ") #totime = time.mktime(converted.timetuple()) return str(converted) #FIXME def get_seconds_difference_reverse(self, toconvert): converted = datetime.datetime.strptime(str(toconvert), "%Y%m%dT%H:%M:%SZ") totime = time.mktime(converted.timetuple()) #FIXME return totime-time.time()-3600 def get_seconds_difference(self, toconvert): converted = datetime.datetime.strptime(str(toconvert), "%Y%m%dT%H:%M:%SZ") totime = time.mktime(converted.timetuple()) #FIXME return time.time()-totime-3600 def get_dmesg(self, ref): return self.connection.host.dmesg(self.session_uuid, ref)["Value"] def restore_server(self, ref, file, name): import httplib #task.createOpaqueRef:149c1416-9934-3955-515a-d644aaddc38fuploadTaskhttp://83.165.161.223/host_restore?session_id=OpaqueRef:149c1416-9934-3955-515a-d644aaddc38f task_uuid = self.connection.task.create(self.session_uuid, "Restoring Server", "Restoring Server %s from %s " % (name,file)) self.track_tasks[task_uuid['Value']] = "Restore.Server" #size=os.stat(file)[6] fp=open(file, 'rb') url = self.wine.selected_ip put.putfile(fp, 'http://' + url + '/host_restore?session_id=%s&task_id=%s&dry_run=true' % (self.session_uuid, task_uuid['Value'])) return conn = httplib.HTTP(url) conn.putrequest('PUT', '/host_restore?session_id=%s&task_id=%s&dry_run=true' % (self.session_uuid, task_uuid['Value'])) conn.putheader('Content-Type', 'text/plain') conn.endheaders() blocknum=0 uploaded=0 blocksize=4096 while not self.halt_import: bodypart=fp.read(blocksize) blocknum+=1 if blocknum % 10 == 0: uploaded+=len(bodypart) if not bodypart: break conn.send(bodypart) fp.close() def save_screenshot(self, ref, filename): url = "http://" + self.wine.selected_ip + '/vncsnapshot?session_id=%s&ref=%s' % (self.session_uuid, ref) urllib.urlretrieve(url, filename) def pool_backup_database(self, ref, filename, name): task_uuid = self.connection.task.create(self.session_uuid, "Backup Pool database", "Backing up database pool " + name) self.track_tasks[task_uuid['Value']] = "Backup.Pool" url = "http://" + self.wine.selected_ip+ '/pool/xmldbdump?session_id=%s&task_id=%s' % (self.session_uuid, task_uuid['Value']) urllib.urlretrieve(url, filename) def pool_restore_database(self, ref, filename, name, dry_run="true"): import httplib task_uuid = self.connection.task.create(self.session_uuid, "Restore Pool database", "Restoring database pool " + filename) self.track_tasks[task_uuid['Value']] = "Restore.Pool" size=os.path.getsize(filename) url = self.wine.selected_ip fp=open(filename, 'r') put.putfile(fp, 'http://' + url + '/pool/xmldbdump?session_id=%s&task_id=%s&dry_run=%s' % (self.session_uuid, task_uuid['Value'], dry_run)) return conn = httplib.HTTP(url) conn.putrequest('PUT', '/pool/xmldbdump?session_id=%s&task_id=%s&dry_run=%s' % (self.session_uuid, task_uuid['Value'], dry_run)) conn.putheader('Content-Length', str(size)) conn.endheaders() total = 0 while True: leido = fp.read(16384) if leido: total += len(leido) time.sleep(0.1) conn.send(leido) else: break fp.close() def host_download_logs(self, ref, filename, name): task_uuid = self.connection.task.create(self.session_uuid, "Downloading host logs", "Downloading logs from host " + name) self.track_tasks[task_uuid['Value']] = "Download.Logs" url = "http://" + self.wine.selected_ip + '/host_logs_download?session_id=%s&sr_id=%s&task_id=%s' % (self.session_uuid, ref, task_uuid['Value']) urllib.urlretrieve(url, filename) def host_download_status_report(self, ref, refs, filename, name): task_uuid = self.connection.task.create(self.session_uuid, "Downloading status report", "Downloading status report from host " + name) self.track_tasks[task_uuid['Value']] = self.host_vm[ref][0] url = "https://" + self.wine.selected_ip + '/system-status?session_id=%s&entries=%s&task_id=%s&output=tar' % (self.session_uuid, refs, task_uuid['Value']) urllib.urlretrieve(url, filename) def backup_server(self, ref, filename, name): task_uuid = self.connection.task.create(self.session_uuid, "Backup Server", "Backing up server " + name) self.track_tasks[task_uuid['Value']] = "Backup.Server" url = "http://" + self.wine.selected_ip + '/host_backup?session_id=%s&sr_id=%s&task_id=%s' % (self.session_uuid, ref, task_uuid['Value']) urllib.urlretrieve(url, filename) def import_vm(self, ref, filename): #file = "pruebadebian.xva" import httplib task_uuid = self.connection.task.create(self.session_uuid, "Importing VM", "Importing VM " + filename) self.track_tasks[task_uuid['Value']] = "Import.VM" size=os.stat(filename)[6] url = self.wine.selected_ip fp=open(filename, 'r') put.putfile(fp, 'http://' + url + '/import?session_id=%s&sr_id=%s&task_id=%s' % (self.session_uuid, ref, task_uuid['Value'])) return conn = httplib.HTTP(url) conn.putrequest('PUT', '/import?session_id=%s&sr_id=%s&task_id=%s' % (self.session_uuid, ref, task_uuid['Value'])) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', str(size)) conn.endheaders() fp=open(file, 'rb') blocknum=0 uploaded=0 blocksize=4096 while not self.halt_import: bodypart=fp.read(blocksize) blocknum+=1 if blocknum % 10 == 0: uploaded+=len(bodypart) if blocknum % 1000 == 0: time.sleep(0.1) if not bodypart: break conn.send(bodypart) fp.close() def add_alert(self, message, ref, list): if message['cls'] == "Host": if message['name'] in messages: parent = list.prepend(None, [gtk.gdk.pixbuf_new_from_file("images/info.gif"), self.hostname, messages_header[message['name']], self.format_date(str(message['timestamp'])), ref, self.host]) list.prepend(parent, [None, "", messages[message['name']] % (self.hostname), "", ref, self.host]) else: parent = list.prepend(None, [gtk.gdk.pixbuf_new_from_file("images/info.gif"), self.hostname, message['name'], self.format_date(str(message['timestamp'])), ref, self.host]) list.prepend(parent, [None, "", message['name'], "", ref, self.host]) elif message['name'] == "ALARM": self.filter_uuid = message['obj_uuid'] if self.vm_filter_uuid() not in self.all_vms: return None if not self.all_vms[self.vm_filter_uuid()]['is_control_domain']: value = message['body'].split("\n")[0].split(" ")[1] dom = xml.dom.minidom.parseString(message['body'].split("config:")[1][1:]) nodes = dom.getElementsByTagName("name") #alert = message['body'].split('value="')[1].split('"')[0] alert = nodes[0].attributes.getNamedItem("value").value nodes = dom.getElementsByTagName("alarm_trigger_level") level = nodes[0].attributes.getNamedItem("value").value nodes = dom.getElementsByTagName("alarm_trigger_period") period = nodes[0].attributes.getNamedItem("value").value if "alert_" + alert in messages: parent = list.prepend(None, [gtk.gdk.pixbuf_new_from_file("images/warn.gif"), self.hostname, messages_header["alert_" + alert], self.format_date(str(message['timestamp'])), ref, self.host]) list.prepend(parent, [None, "", messages["alert_" + alert] % \ (self.all_vms[self.vm_filter_uuid()]['name_label'], float(value)*100, int(period), float(level)*100), "", ref, self.host]) else: print message['name'] print message['body'] else: value = message['body'].split("\n")[0].split(" ")[1] alert = message['body'].split('value="')[1].split('"')[0] if "host_alert_" + alert in messages: parent = list.prepend(None, [gtk.gdk.pixbuf_new_from_file("images/warn.gif"), self.hostname, messages_header["host_alert_" + alert] % ("Control Domain"), self.format_date(str(message['timestamp'])), ref, self.host]) list.prepend(parent, [None, "", messages["host_alert_" + alert] % \ ("Control Domain", self.hostname, float(value)), "", ref, self.host]) else: print message['name'] print message['body'] def add_vm_to_tree(self, vm): if self.all_vms[vm]['resident_on'] != "OpaqueRef:NULL" and self.all_vms[vm]['resident_on'] in self.hostroot: resident = self.all_vms[vm]['resident_on'] self.treestore.prepend(self.hostroot[self.all_vms[vm]['resident_on']], [\ gtk.gdk.pixbuf_new_from_file("images/tree_%s_16.png"\ % self.all_vms[vm]['power_state'].lower()),\ self.all_vms[vm]['name_label'], self.all_vms[vm]['uuid'],\ "vm", self.all_vms[vm]['power_state'], self.host, vm, self.all_vms[vm]['allowed_operations'], self.all_hosts[resident]['address']]) elif self.all_vms[vm]['affinity'] != "OpaqueRef:NULL" and self.all_vms[vm]['affinity'] in self.hostroot: affinity = self.all_vms[vm]['affinity'] self.treestore.prepend(self.hostroot[self.all_vms[vm]['affinity']], [\ gtk.gdk.pixbuf_new_from_file("images/tree_%s_16.png"\ % self.all_vms[vm]['power_state'].lower()),\ self.all_vms[vm]['name_label'], self.all_vms[vm]['uuid'],\ "vm", self.all_vms[vm]['power_state'], self.host, vm, self.all_vms[vm]['allowed_operations'], self.all_hosts[affinity]['address']]) else: if self.poolroot: self.treestore.prepend(self.poolroot, [\ gtk.gdk.pixbuf_new_from_file("images/tree_%s_16.png"\ % self.all_vms[vm]['power_state'].lower()),\ self.all_vms[vm]['name_label'], self.all_vms[vm]['uuid'],\ "vm", self.all_vms[vm]['power_state'], self.host, vm, self.all_vms[vm]['allowed_operations'], self.host]) else: self.treestore.prepend(self.hostroot[self.all_hosts.keys()[0]], [\ gtk.gdk.pixbuf_new_from_file("images/tree_%s_16.png"\ % self.all_vms[vm]['power_state'].lower()),\ self.all_vms[vm]['name_label'], self.all_vms[vm]['uuid'],\ "vm", self.all_vms[vm]['power_state'], self.host, vm, self.all_vms[vm]['allowed_operations'], self.host]) def fill_allowed_operations(self, ref): actions = self.connection.VM.get_allowed_operations(self.session_uuid, ref)['Value'] self.all_vms[ref]['allowed_operations'] = actions return actions def fill_vm_network(self, ref, tree, list): #self.filter_ref = ref #vm_vifs = filter(self.filter_vif_ref, self.all_vif.values()) list.clear() if ref in self.all_vms: guest_metrics = self.all_vms[ref]['guest_metrics'] for vif_ref in self.all_vms[ref]['VIFs']: vif = self.all_vif[vif_ref] if "kbps" in vif['qos_algorithm_params']: limit = vif['qos_algorithm_params']['kbps'] else: limit = "" ip = "" if guest_metrics in self.all_vm_guest_metrics and vif['device'] + "/ip" in self.all_vm_guest_metrics[guest_metrics]['networks']: ip = self.all_vm_guest_metrics[guest_metrics]['networks'][vif['device'] + "/ip"] else: ip = "" #FIXME if vif['network'] in self.all_network: network = self.all_network[vif['network']]['name_label'].replace('Pool-wide network associated with eth','Network ') else: network = "" list.append((vif['device'], \ vif['MAC'], \ limit, \ network, \ ip, \ str(vif['currently_attached']), vif_ref)) else: print "VM not found %s" % ref def set_vif_limit(self, ref, limit, vm_ref): qos_algorithm_params = { 'kbps': str(limit) } res = self.connection.VIF.set_qos_algorithm_params(self.session_uuid, ref, qos_algorithm_params) if "Value" in res: self.track_tasks[res['Value']] = vm_ref else: print res def set_vif_to_manual(self, ref, vm_ref): res = self.connection.VIF.set_MAC_autogenerated(self.session_uuid, ref, False) if "Value" in res: self.track_tasks[res['Value']] = vm_ref else: print res def fill_vm_snapshots(self, uuid, tree=None, list=None): list.clear() if uuid in self.all_vms: all_snapshots = self.all_vms[uuid]['snapshots'] for snapshot_uuid in all_snapshots: snapshot_name = self.all_vms[snapshot_uuid]['name_label'] snapshot_time = self.all_vms[snapshot_uuid]['snapshot_time'] snapshot_of = self.all_vms[snapshot_uuid]['snapshot_of'] snapshot_size = 0 for vbd in self.all_vms[snapshot_uuid]['VBDs']: vbd_data = self.all_vbd[vbd] if vbd_data['type'] == 'Disk': snapshot_size += int(self.connection.VDI.get_record(self.session_uuid,vbd_data['VDI'])['Value']['physical_utilisation']) list.append([snapshot_uuid, "" + snapshot_name + "\n\nTaken on: " + str(snapshot_time) + "\n\nSize: " + \ self.convert_bytes(snapshot_size) + "\n\n" + "Used by: " + self.wine.selected_name + "\n"]) def update_performance(self, uuid, ref, ip, host=False, period=5): # Default three hours of period self.halt_performance = False for widget in ["scrolledwindow47", "scrolledwindow48", "scrolledwindow49", "scrolledwindow50"]: if self.wine.builder.get_object(widget).get_children()[0].get_children(): #gobject.idle_add(lambda: self.wine.builder.get_object(widget).get_children()[0].remove(self.wine.builder.get_object(widget).get_children()[0].get_children()[0]) and False) gtk.gdk.threads_enter() self.wine.builder.get_object(widget).get_children()[0].remove(self.wine.builder.get_object(widget).get_children()[0].get_children()[0]) gtk.gdk.threads_leave() if host: data_sources = self.connection.host.get_data_sources(self.session_uuid, ref) else: data_sources = self.connection.VM.get_data_sources(self.session_uuid, ref) if not "Value" in data_sources: return data_sources = data_sources['Value'] ds = {} for data_source in data_sources: if data_source['enabled']: name = data_source['name_label'] desc = data_source['name_description'] if not name[:3] in ds.keys(): ds[name[:3]] = [] if ds[name[:3]].count([name, desc]) == 0: if name not in ("memory_internal_free", "xapi_free_memory_kib", "xapi_memory_usage_kib", "xapi_live_memory_kib") \ and name[:6] != "pif___": ds[name[:3]].append([name, desc]) if host: if os.path.exists(os.path.join(self.wine.pathconfig, "host_rrds.rrd")): os.unlink(os.path.join(self.wine.pathconfig, "host_rrds.rrd")) urllib.urlretrieve("http://%s/host_rrds?session_id=%s" % (ip, self.session_uuid), os.path.join(self.wine.pathconfig, "host_rrds.rrd")) rrd = RRD(os.path.join(self.wine.pathconfig, "host_rrds.rrd")) else: if os.path.exists(os.path.join(self.wine.pathconfig, "vm_rrds.rrd")): os.unlink(os.path.join(self.wine.pathconfig, "vm_rrds.rrd")) urllib.urlretrieve("http://%s/vm_rrds?session_id=%s&uuid=%s" % (ip, self.session_uuid, uuid), os.path.join(self.wine.pathconfig, "vm_rrds.rrd")) rrd = RRD(os.path.join(self.wine.pathconfig, "vm_rrds.rrd")) rrdinfo = rrd.get_data(period) def show_tic(value): if time.strftime("%S", time.localtime(value)) == "00": return time.strftime("%H:%M", time.localtime(value)) else: return "" def hovered(chart, graph, (x, y)): #print chart.get_title() self.wine.builder.get_object("lblperf" + graph.get_title()[:3].lower()).set_label( "%s - %s = %0.2f" % (time.strftime("%d/%m %H:%M:%S", time.localtime(x)), graph.get_title(), y)) # Chart chart = {} graph = {} for name in ["cpu", "vbd", "vif", "mem"]: chart[name] = line_chart.LineChart() chart[name].xaxis.set_show_tics(True) chart[name].xaxis.set_tic_format_function(show_tic) chart[name].yaxis.set_position(7) chart[name].connect("datapoint-hovered", hovered) chart[name].legend.set_visible(True) chart[name].legend.set_position(line_chart.POSITION_BOTTOM_RIGHT) chart[name].set_padding(100) chart[name].yaxis.set_label("kBps") chart["cpu"].yaxis.set_label("%") chart["mem"].yaxis.set_label("MB") # CPU Graph chart["cpu"].set_yrange((0, 100)) for key in rrdinfo.keys(): if key[:3] == "cpu": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]*100 graph[key] = line_chart.Graph(key, key, data) graph[key].set_show_title(False) chart["cpu"].add_graph(graph[key]) chart["cpu"].set_size_request(len(data)*20, 250) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow47").add_with_viewport(chart["cpu"]) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow47").show_all() and False) # Memory if "memory_internal_free" in rrdinfo and "memory" in rrdinfo: chart["mem"].set_yrange((0, int(rrdinfo["memory"]["max_value"])/1024/1024)) data = rrdinfo["memory"]["values"] data2 = rrdinfo["memory_internal_free"]["values"] for i in range(len(data2)): data[i][1] = (data[i][1] - data2[i][1]*1024)/1024/1024 graph["mem"] = line_chart.Graph("Memory used", "Memory used", data) graph["mem"].set_show_title(False) chart["mem"].add_graph(graph["mem"]) chart["mem"].set_size_request(len(data)*20, 250) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").add_with_viewport(chart["mem"]) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").show_all() and False) elif "memory_total_kib" in rrdinfo and "xapi_free_memory_kib" in rrdinfo: chart["mem"].set_yrange((0, int(rrdinfo["memory_total_kib"]["max_value"])/1024/1024)) data = rrdinfo["memory_total_kib"]["values"] data2 = rrdinfo["xapi_free_memory_kib"]["values"] for i in range(len(data2)): data[i][1] = (data[i][1] - data2[i][1]*1024)/1024/1024 graph["mem"] = line_chart.Graph("Memory used", "Memory used", data) graph["mem"].set_show_title(False) chart["mem"].add_graph(graph["mem"]) chart["mem"].set_size_request(len(data)*20, 250) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").add_with_viewport(chart["mem"]) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").show_all() and False) else: label = gtk.Label() label.set_markup("No data availaible") gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").add_with_viewport(label) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow48").show_all() and False) # Network max_value = 0 data = None for key in rrdinfo.keys(): if key[:3] == "vif" or key[:3] == "pif": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]/1024 if data[i][1] > max_value: max_value = data[i][1] graph[key] = line_chart.Graph(key, key, data) graph[key].set_show_title(False) chart["vif"].add_graph(graph[key]) if data: chart["vif"].set_yrange((0, max_value)) chart["vif"].set_size_request(len(data)*20, 250) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow49").add_with_viewport(chart["vif"]) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow49").show_all() and False) else: label = gtk.Label() label.set_markup("No data availaible") gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow49").add_with_viewport(label) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow49").show_all() and False) # Disk if not host: max_value = 0 data = None for key in rrdinfo.keys(): if key[:3] == "vbd": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]/1024 graph[key] = line_chart.Graph(key, key, data) graph[key].set_show_title(False) chart["vbd"].add_graph(graph[key]) if rrdinfo[key]['max_value']/1024 > max_value: max_value = rrdinfo[key]['max_value']/1024 chart["vbd"].set_yrange((0, max_value)) chart["vbd"].set_size_request(len(data)*20, 250) if data: gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow50").add_with_viewport(chart["vbd"]) and False) gobject.idle_add(lambda: self.wine.builder.get_object("scrolledwindow50").show_all() and False) if max_value == 0: max_value = 1 gobject.idle_add(lambda: self.wine.adjust_scrollbar_performance() and False) time.sleep(5) while not self.halt_performance: if os.path.exists(os.path.join(self.wine.pathconfig, "update.rrd")): os.unlink(os.path.join(self.wine.pathconfig, "update.rrd")) urllib.urlretrieve("http://%s/rrd_updates?session_id=%s&start=%s&cf=AVERAGE&interval=5&vm_uuid=%s" % (ip, self.session_uuid, int(time.time())-10, uuid), os.path.join(self.wine.pathconfig, "update.rrd")) rrd = XPORT(os.path.join(self.wine.pathconfig, "update.rrd")) rrdinfo = rrd.get_data() for key in rrdinfo: if key in graph: if rrdinfo[key]['values']: if key[:3] == "cpu": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]*100 graph[key].add_data(data) chart[key[:3]].queue_draw() elif key[:3] == "vif": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]/1024 graph[key].add_data(data) chart[key[:3]].queue_draw() elif key[:3] == "vbd": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]/1024 graph[key].add_data(data) chart[key[:3]].queue_draw() if "memory_internal_free" in rrdinfo: data = rrdinfo["memory"]["values"] data2 = rrdinfo["memory_internal_free"]["values"] for i in range(len(data2)): data[i][1] = (data[i][1] - data2[i][1]*1024)/1024/1024 graph["mem"].add_data(data) chart["mem"].queue_draw() for i in range(5): if not self.halt_performance: time.sleep(1) def fill_vm_log(self, uuid, tree=None, list=None, thread=False): self.filter_uuid = uuid self.filter_ref = self.wine.selected_ref i = 0 for ch in self.wine.builder.get_object("vmtablelog").get_children(): gobject.idle_add(lambda: self.wine.builder.get_object("vmtablelog").remove(ch) and False) for task_ref in filter(self.task_filter_uuid, self.tasks): task = self.all_tasks[task_ref] if "snapshot" in task: self.add_box_log(task['snapshot']['name_label'], str(task['snapshot']['created']), "%s %s" % (task["snapshot"]["name_label"], self.all_vms[self.track_tasks[task["ref"]]]["name_label"]), str(task['snapshot']['created']), task['ref'], task, float(task['snapshot']['progress']),i%2) else: if "ref" in task: self.add_box_log(task['name_label'], str(task['created']), "%s %s" % (task["name_label"], self.all_vms[self.track_tasks[task["ref"]]]["name_label"]), str(task['created']), self.get_task_ref_by_uuid(task['uuid']), task, float(task['progress']),i%2) else: self.add_box_log(task['name_label'], str(task['created']), "%s %s" % (task["name_label"], task["name_description"]), str(task['created']), task_ref, task, float(task['progress']),i%2) i = i + 1 for log in sorted(filter(self.log_filter_uuid, self.all_messages.values()), key=itemgetter("timestamp"), reverse=True): timestamp = str(log['timestamp']) if thread: gobject.idle_add(lambda: self.add_box_log(log['name'], timestamp, log['body'], str(log['timestamp']),alt=i%2) and False) else: self.add_box_log(log['name'], timestamp, log['body'], str(log['timestamp']),alt=i%2) i = i + 1 def add_box_log(self, title, date, description, time, id=None, task=None, progress=0, alt=0): date = self.format_date(date) vboxframe = gtk.Frame() vboxframe.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#d5e5f7")) if task: vboxframe.set_size_request(700,100) else: vboxframe.set_size_request(700,80) vboxchild = gtk.Fixed() vboxevent = gtk.EventBox() vboxevent.add(vboxchild) vboxframe.add(vboxevent) vboxchildlabel1 = gtk.Label() vboxchildlabel1.set_selectable(True) vboxchildlabel2 = gtk.Label() vboxchildlabel2.set_selectable(True) vboxchildlabel3 = gtk.Label() vboxchildlabel3.set_selectable(True) vboxchildlabel3.set_size_request(600,-1) vboxchildlabel3.set_line_wrap(True) vboxchildlabel4 = gtk.Label() vboxchildlabel4.set_selectable(True) #FIXME #vboxchildprogressbar.set_style(1) vboxchildlabel2.set_label(date) if title in messages_header: vboxchildlabel1.set_label(messages_header[title]) else: vboxchildlabel1.set_label(title) if title in messages: vboxchildlabel3.set_label(messages[title] % (self.wine.selected_name)) else: vboxchildlabel3.set_label(description) vboxchildlabel1.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) #vboxchildlabel4.set_label(time) vboxchild.put(vboxchildlabel1, 25, 12) vboxchild.put(vboxchildlabel2, 500, 12) vboxchild.put(vboxchildlabel3, 25, 32) vboxchild.put(vboxchildlabel4, 25, 52) # Active task if task: self.vboxchildcancel[id] = gtk.Button() self.vboxchildcancel[id].connect("clicked", self.cancel_task) self.vboxchildcancel[id].set_name(id) self.vboxchildprogressbar[id] = gtk.ProgressBar() self.vboxchildprogress[id] = gtk.Label() self.vboxchildprogress[id].set_selectable(True) self.vboxchildprogressbar[id].set_size_request(500,20) self.vboxchildprogressbar[id].set_fraction(progress) if ("snapshot" in task and (task["snapshot"]["status"] != "failure" and task["snapshot"]["status"] != "success")) or \ (task["status"] != "failure" and task["status"] != "success"): vboxchild.put(self.vboxchildcancel[id], 500, 32) self.vboxchildcancel[id].set_label("Cancel") self.vboxchildprogress[id].set_label("Progress: ") vboxchild.put(self.vboxchildprogressbar[id], 100, 72) elif ("snapshot" in task and task["snapshot"]["status"] == "failure") or task["status"] == "failure": self.vboxchildcancel[id].hide() self.vboxchildprogressbar[id].hide() self.vboxchildprogress[id].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FF0000')) if "snapshot" in task: self.vboxchildprogress[id].set_label("Error: %s" % task["snapshot"]["error_info"]) else: self.vboxchildprogress[id].set_label("Error: %s" % task["error_info"]) else: if ("snapshot" in task and task["snapshot"]["finished"]) or task["finished"]: vboxchildlabel4.set_label("Finished: %s" % self.format_date(str(task["finished"]))) vboxchild.put(self.vboxchildprogress[id], 25, 72) if ("snapshot" in task and task["snapshot"]["status"] == "success"): self.vboxchildcancel[id].hide() self.vboxchildprogressbar[id].hide() if task["status"] == "success": self.vboxchildcancel[id].hide() self.vboxchildprogressbar[id].hide() if alt: vboxevent.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#d5e5f7")) else: vboxevent.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#BAE5D3")) self.wine.builder.get_object("vmtablelog").add(vboxframe) self.wine.builder.get_object("vmtablelog").show_all() def cancel_task(self, widget, data=None): self.connection.task.cancel(self.session_uuid, gtk.Buildable.get_name(widget)) widget.hide() self.vboxchildprogress[gtk.Buildable.get_name(widget)].set_label("Cancelled") self.vboxchildprogressbar[gtk.Buildable.get_name(widget)].hide() self.wine.push_alert("Task cancelled") def fill_host_storage(self, ref, list): list.clear() for storage in self.all_storage.values(): on_host = False for pbd in storage['PBDs']: if self.all_pbd[pbd]['host'] == ref: on_host = True #if storage['type'] != "iso": if on_host: if "physical_size" in storage: if int(storage['physical_size']) > 0: usage = "%d%% (%s used)" % \ (((float(storage['physical_utilisation'])/1073741824) / \ (float(storage['physical_size'])/1073741824) * 100), \ self.convert_bytes(storage['physical_utilisation'])) else: usage = "0% (0B Used)" if storage['name_label'] != "XenServer Tools": list.append((storage['name_label'], storage['name_description'], storage['type'], str(storage['shared']), usage, self.convert_bytes(storage['physical_size']), self.convert_bytes(storage['virtual_allocation']))) def fill_host_search(self, ref, list): while not self.halt_search: gobject.idle_add(lambda: list.clear() and False) position = 0 hosts = {} #FIXME: what happen when a pool exists? for host in self.all_hosts.keys(): metrics = self.all_hosts[host]['metrics'] memory_free = int(self.all_host_metrics[metrics]['memory_free']) memory_total = int(self.all_host_metrics[metrics]['memory_total']) if memory_total == 0: memory = "" memory_img = 0 else: memory = str(((memory_total-memory_free)*100)/memory_total) + "% used of " + self.convert_bytes(memory_total) memory_img = int((((memory_total-memory_free)*100)/memory_total)/10) start_time = self.all_hosts[host]['other_config']['boot_time'][:-1] uptime = self.humanize_time(time.time() - int(start_time)) hosts[host] = position gobject.idle_add(lambda item: list.append(None, item) and False, ([gtk.gdk.pixbuf_new_from_file("images/tree_connected_16.png"), "" + self.all_hosts[host]['name_label'] + "\n" + self.all_hosts[host]['name_description'] + "", gtk.gdk.pixbuf_new_from_file("images/usagebar_5.png"), "",gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % str(memory_img)),memory,"-","",self.all_hosts[host]['address'],uptime, None])) position = position + 1 for host in self.all_hosts.keys(): Thread(target=self.fill_vm_search, args=(host,list,hosts)).start() for i in range(0,60): if not self.halt_search: time.sleep(1) def fill_vm_search(self, host,list, hosts): rrd_updates = rrdinfo.RRDUpdates("http://%s/rrd_updates?session_id=%s&start=%d&cf=AVERAGE&interval=5&host=true" % (self.all_hosts[host]["address"], self.session_uuid, time.time()-600)) rrd_updates.refresh() for uuid in rrd_updates.get_vm_list(): for vm in self.all_vms: if self.all_vms[vm]["uuid"] == uuid: break guest_metrics = self.all_vms[vm]['guest_metrics'] ips = [] with_tools = True if guest_metrics != "OpaqueRef:NULL": for vif in self.all_vms[vm]['VIFs']: if "networks" in self.all_vm_guest_metrics[guest_metrics]: if self.all_vif[vif]['device'] + "/ip" in self.all_vm_guest_metrics[guest_metrics]['networks']: if self.all_vm_guest_metrics[guest_metrics]['networks'][self.all_vif[vif]['device'] + "/ip"]: ips.append(self.all_vm_guest_metrics[guest_metrics]['networks'][self.all_vif[vif]['device'] + "/ip"]) else: with_tools = False cpu = 0 cpu_pct = 0 vbd_write_avg = 0 vbd_write_max = 0 vbd_read_avg = 0 vbd_read_max = 0 vif_write_avg = 0 vif_write_max = 0 vif_read_avg = 0 vif_read_max = 0 memory = 0 memory_total = 0 for param in rrd_updates.get_vm_param_list(uuid): data=[0] media=0.0 i = 0 row = None for row in range(rrd_updates.get_nrows()): value1 = rrd_updates.get_vm_data(uuid,param,row) if value1 != "NaN": data.append(value1) media += value1 i += 1 if i == 0: i=1 if row: if param.count("cpu") > 0: cpu = cpu + 1 cpu_pct = cpu_pct + int(rrd_updates.get_vm_data(uuid,param,row)*100) elif param.count("vbd") > 0 and param.count("write"): try: vbd_write_avg += int((media/i)/1024) vbd_write_max += int(max(data)/1024) except: vbd_write_avg += 0 vbd_write_max += 0 elif param.count("vbd") > 0 and param.count("read"): try: vbd_read_avg += int((media/i)/1024) vbd_read_max += int(max(data)/1024) except: vbd_read_avg += 0 vbd_read_max += 0 elif param.count("vif") > 0 and param.count("tx"): try: vif_write_avg += int((media/i)/1024) vif_write_max += int(max(data)/1024) except: vif_write_avg += 0 vif_write_max += 0 elif param.count("vif") > 0 and param.count("rx"): try: vif_read_avg += int((media/i)/1024) vif_read_max += int(max(data)/1024) except: vif_read_avg += 0 vif_read_max += 0 elif param.count("memory_internal_free") > 0: memory = int(rrd_updates.get_vm_data(uuid,param,row))*1024 memory_total = int(self.all_vms[vm]['memory_dynamic_max']) else: #print str(media/i) + "/" + str(max(data)) #print "last: " + str(rrd_updates.get_vm_data(uuid,param,row)) pass if cpu: load = str(cpu_pct/cpu) load_img = str(int((cpu_pct/cpu)/10)) else: load = "0" load_img = "0" if memory: memory_used = str(((memory_total-memory)*100)/memory_total) memory_img = str(int(((memory_total-memory)*100)/memory_total)/10) else: memory_used = "0" memory_img = "0" if row: parent = self.all_vms[vm]['resident_on'] if parent == "OpaqueRef:NULL": parent = self.all_vms[vm]['affinity'] if not self.all_vms[vm]['is_control_domain']: if self.all_vms[vm]['metrics'] not in self.all_vm_metrics: self.all_vm_metrics[self.all_vms[vm]['metrics']] = self.connection.VM_metrics.get_record(self.session_uuid, self.all_vms[vm]['metrics'])['Value'] start_time = self.all_vm_metrics[self.all_vms[vm]['metrics']]['start_time'] uptime = self.humanize_time(self.get_seconds_difference(start_time)) if parent != "OpaqueRef:NULL": if int(load_img) > 10: load_img = "10" elif int(load_img) < 0: load_img = "0" if int(memory_img) > 10: memory_img = "10" elif int(memory_img) < 0: memory_img = "0" if with_tools: gobject.idle_add(lambda parent_path, item: list.append(list.get_iter(parent_path), item) and False, hosts[parent], ([gtk.gdk.pixbuf_new_from_file("images/tree_running_16.png"), self.all_vms[vm]['name_label'] + "\n" + self.all_vms[vm]['name_description'] + "", gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % load_img), load + "% of " + str(cpu) + " cpus", gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % abs(int(memory_img))), memory_used + "% of " + self.convert_bytes(memory_total), str(vbd_write_avg) + "/" + str(vbd_write_max) + " | " + str(vbd_read_avg) + "/" + str(vbd_read_max), str(vif_write_avg) + "/" + str(vif_write_max) + " | " + str(vif_read_avg) + "/" + str(vif_read_max), "\n".join(ips), uptime, None ])) else: gobject.idle_add(lambda parent_path, item: list.append(list.get_iter(parent_path), item) and False, hosts[parent], ([gtk.gdk.pixbuf_new_from_file("images/tree_running_16.png"), self.all_vms[vm]['name_label'] + "\n" + self.all_vms[vm]['name_description'] + "", gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % load_img), load + "% of " + str(cpu) + " cpus", gtk.gdk.pixbuf_new_from_file("images/usagebar_0.png"), "", "XenServer tools", "not installed", "-", uptime, None ])) else: pass """ list.append(None, ([gtk.gdk.pixbuf_new_from_file("images/tree_running_16.png"), self.all_vms[vm]['name_label'] + "\n" + self.all_vms[vm]['name_description'] + "", gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % load_img), load + "% of " + str(cpu) + " cpus", gtk.gdk.pixbuf_new_from_file("images/usagebar_0.png"), "", "XenServer tools", "not installed", "-", uptime, None ])) """ #print self.all_vms[vm] else: gobject.idle_add(lambda: list.set(list.get_iter(hosts[parent]), 2, gtk.gdk.pixbuf_new_from_file("images/usagebar_%s.png" % load_img), 3, load + "% of " + str(cpu) + " cpus", 7, str(vif_write_avg) + "/" + str(vif_write_max) + " | " + str(vif_read_avg) + "/" + str(vif_read_max)) and False) gobject.idle_add(lambda: self.wine.treesearch.expand_all() and False) def fill_local_storage(self, ref, list): list.clear() """ for pbd in self.all_storage[ref]['PBDs']: print self.all_pbd[pbd] print "*************" """ if ref in self.all_storage: for vdi in self.all_storage[ref]['VDIs']: pct = (int(self.all_vdi[vdi]['physical_utilisation'])/int(self.all_vdi[vdi]['virtual_size']))*100 if self.all_vdi[vdi]['VBDs']: vbd = self.all_vbd[self.all_vdi[vdi]['VBDs'][0]] vm = self.all_vms[vbd['VM']]['name_label'] else: vm = "" if self.all_vdi[vdi]['is_a_snapshot']: vm += " (snapshot)" #FIXME if self.all_vdi[vdi]['name_label'] != "base copy": list.append([vdi, self.all_vdi[vdi]['name_label'], self.all_vdi[vdi]['name_description'], \ self.convert_bytes(self.all_vdi[vdi]['virtual_size']) + " (" + str(pct) + "% on disk)", vm]) def fill_vm_storage(self, ref, list): self.filter_ref = ref all_vbds = filter(self.filter_vbd_ref, self.all_vbd.values()) list.clear() if ref not in self.all_vms: return for vbd_ref in self.all_vms[ref]['VBDs']: vbd = self.all_vbd[vbd_ref] if vbd['VDI'] != "OpaqueRef:NULL" and vbd['type'] != "CD": if vbd['mode'] == "RW": ro = "False" else: ro = "True" if vbd['VDI']: self.filter_vdi = vbd['VDI'] vdi = self.all_vdi[self.filter_vdi_ref()] vdi_name_label = vdi['name_label'] vdi_name_description = vdi['name_description'] vdi_virtual_size = vdi['virtual_size'] vdi_sr = vdi['SR'] sr_name = self.all_storage[vdi_sr]['name_label'] list.append((vdi_name_label, \ vdi_name_description, \ sr_name, \ vbd['userdevice'], \ self.convert_bytes(vdi_virtual_size), \ ro, \ "0 (Highest) ", \ str(vbd['currently_attached']), \ "/dev/" + vbd['device'], vbd['VDI'], vbd_ref, vbd['bootable'])) def fill_vm_storage_dvd(self, ref, list): i = 0 active = 0 self.filter_ref = ref all_vbds = filter(self.filter_vbd_ref, self.all_vbd.values()) vmvdi = "" for vbd in all_vbds: if vbd['type'] == "CD": vmvdi = vbd['VDI'] list.clear() list.append(["", "empty", True, True]) list.append(["DVD drives", "", False, True]) for sr in self.all_storage: if self.all_storage[sr]['type'] == "udev" and self.all_storage[sr]['sm_config']["type"] == "cd": if len(self.all_storage[sr]['VDIs']): i = i + 1 if self.all_storage[sr]['VDIs'][0] == vmvdi: active = i if self.all_storage[sr]['VDIs'][0] in self.all_vdi: info = self.all_vdi[self.all_storage[sr]['VDIs'][0]] list.append(["\tDVD Drive " + info['location'][-1:], self.all_storage[sr]['VDIs'][0], True, False]) else: list.append(["\tDVD Drive", self.all_storage[sr]['VDIs'][0], True, False]) for sr in self.all_storage: if self.all_storage[sr]['type'] == "iso": list.append([self.all_storage[sr]['name_label'], sr, False, True]) i = i + 1 isos = {} for vdi in self.all_storage[sr]['VDIs']: isos[str(self.all_vdi[vdi]['name_label'])] = vdi for vdi_ref in sorted(isos): vdi = isos[vdi_ref] list.append(["\t" + self.all_vdi[vdi]['name_label'], vdi, True, False]) i = i + 1 if vdi == vmvdi: active = i if active == 0: return active else: return active+1 def update_tab_storage(self, ref, builder): labels = {} labels['lblstgname'] = self.all_storage[ref]['name_label'] labels['lblstgdescription'] = self.all_storage[ref]['name_description'] labels['lblstgtags'] = ", ".join(self.all_storage[ref]['tags']) stg_other_config = self.all_storage[ref]['other_config'] if "folder" in stg_other_config: labels['lblstgfolder'] = stg_other_config['folder'] else: labels['lblstgfolder'] = "" labels['lblstgtype'] = self.all_storage[ref]['type'].upper() labels['lblstgsize'] = "%s used of %s total (%s allocated)" % \ (self.convert_bytes(self.all_storage[ref]['physical_utilisation']), self.convert_bytes(self.all_storage[ref]['physical_size']), self.convert_bytes(self.all_storage[ref]['virtual_allocation']) ) if "devserial" in self.all_storage[ref]['sm_config']: devserial = self.all_storage[ref]['sm_config']['devserial'].split("-",2) labels['lblstgserial'] = devserial[0].upper() + " ID:" if len(devserial) > 1: labels['lblstgscsi'] = devserial[1] else: labels['lblstgscsi'] = devserial[0] else: labels['lblstgscsi'] = "" broken = False # Fix using PBD and "currently_attached" if len(self.all_storage[ref]['PBDs']) == 0: broken = True labels['lblstgstate'] = "Detached" labels['lblstghostcon'] = "Connection Missing" else: broken = False for pbd_ref in self.all_storage[ref]['PBDs']: if not self.all_pbd[pbd_ref]['currently_attached']: labels['lblstgstate'] = "Broken" labels['lblstghostcon'] = "Unplugged" broken = True if not broken: if len(self.all_storage[ref]['PBDs']) > 0: labels['lblstgstate'] = "OK" labels['lblstghostcon'] = "Connected" """ elif len(self.all_storage[ref]['PBDs']) > 0: labels['lblstgstate'] = "Dettached" labels['lblstghostcon'] = "Connection Missing" """ labels['lblstghost'] = self.wine.selected_host if len(self.all_storage[ref]['PBDs']) == 0: labels['lblstgmultipath'] = "No" else: pbd = self.all_pbd[self.all_storage[ref]['PBDs'][0]] if "multipathed" in pbd['other_config'] and pbd['other_config']["multipathed"] == "true": if "SCSIid" in pbd['device_config']: #{'uuid': '232b7d15-d8cb-e183-3838-dfd33f6bd597', 'SR': 'OpaqueRef:1832f6e1-73fa-b43d-fcd2-bac969abf867', 'other_config': {'mpath-3600a0b8000294d50000045784b85e36f': '[1, 1, -1, -1]', 'multipathed': 'true'}, 'host': 'OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338d', 'currently_attached': True, 'device_config': {'SCSIid': '3600a0b8000294d50000045784b85e36f'}} scsiid = pbd['device_config']["SCSIid"] paths = eval(pbd["other_config"]["mpath-" + scsiid]) if paths[0] == paths[1]: labels['lblstgmultipath'] = "%s of %s paths active" % (paths[0], paths[1]) else: labels['lblstgmultipath'] = "%s of %s paths active" % (paths[0], paths[1]) else: labels['lblstgmultipath'] = "Yes" else: labels['lblstgmultipath'] = "No" for label in labels.keys(): builder.get_object(label).set_label(labels[label]) def is_storage_broken(self, ref): for pbd_ref in self.all_storage[ref]['PBDs']: if not self.all_pbd[pbd_ref]['currently_attached']: return True return False def update_tab_template(self, ref, builder): labels = {} labels['lbltplname'] = self.all_vms[ref]['name_label'] labels['lbltpldescription'] = self.all_vms[ref]['name_description'] if not self.all_vms[ref]['HVM_boot_policy']: labels['lbltplboot'] = "Boot order:" labels["lbltplparameters"] = self.all_vms[ref]['PV_args'] else: labels['lbltplboot'] = "OS boot parameters:" labels['lbltplparameters'] = "" for param in list(self.all_vms[ref]['HVM_boot_params']['order']): if param == 'c': labels['lbltplparameters'] += "Hard Disk\n" elif param == 'd': labels['lbltplparameters'] += "DVD-Drive\n" elif param == 'n': labels['lbltplparameters'] += "Network\n" other_config = self.all_vms[ref]['other_config'] if "folder" in other_config: labels['lbltplfolder'] = other_config['folder'] else: labels['lbltplfolder'] = "" labels["lbltplmemory"] = self.convert_bytes(self.all_vms[ref]['memory_dynamic_max']) if self.all_vms[ref]['tags']: labels["lbltpltags"] = ", ".join(self.all_vms[ref]['tags']) else: labels["lbltpltags"] = "" labels["lbltplcpu"] = self.all_vms[ref]['VCPUs_at_startup'] if "auto_poweron" in other_config and other_config["auto_poweron"] == "true": labels["lbltplautoboot"] = "Yes" else: labels["lbltplautoboot"] = "No" priority = self.all_vms[ref]["VCPUs_params"] if "weight" in priority: #labels["lbltplpriority"] = priority['weight'] weight = priority['weight'] if weight == 1: labels["lbltplpriority"] = "Lowest" elif weight <= 4: labels["lbltplpriority"] = "Very Low" elif weight <= 32: labels["lbltplpriority"] = "Low" elif weight <= 129: labels["lbltplpriority"] = "Below Normal" elif weight <= 512: labels["lbltplpriority"] = "Normal" elif weight <= 2048: labels["lbltplpriority"] = "Above Normal" elif weight <= 4096: labels["lbltplpriority"] = "High" elif weight <= 16384: labels["lbltplpriority"] = "Very High" else: labels["lbltplpriority"] = "Highest" else: labels["lbltplpriority"] = "Normal" #FIXME #labels["lblvmstartup"] = str(self.connection.VM_metrics.get_start_time(self.session_uuid,metric)['Value']) metric = self.all_vms[ref]['metrics'] if metric not in self.all_vm_metrics: res = self.connection.VM_metrics.get_record(self.session_uuid, ref) if "Value" in res: self.all_vm_metrics[ref] = res["Value"] for label in labels.keys(): builder.get_object(label).set_label(labels[label]) pass def update_tab_host_general(self, ref, builder): labels = {} software_version = self.all_hosts[ref]['software_version'] license_params = self.all_hosts[ref]['license_params'] labels['lblhostname'] = self.all_hosts[ref]['name_label'] labels['lblhostdescription'] = self.all_hosts[ref]['name_description'] labels['lblhosttags'] = ", ".join(self.all_hosts[ref]['tags']) host_other_config = self.all_hosts[ref]['other_config'] if "folder" in host_other_config: labels['lblhostfolder'] = host_other_config['folder'] else: labels['lblhostfolder'] = "" # FIXME if "iscsi_iqn" in host_other_config: labels['lblhostiscsi'] = host_other_config['iscsi_iqn'] else: labels['lblhostiscsi'] = "" #FIXME labels['lblhostpool'] = "" #str(self.connection.session.get_pool( # self.session_uuid, self.session['Value'])['Value']) logging = self.all_hosts[ref]['logging'] if "syslog_destination" in logging: labels['lblhostlog'] = logging['syslog_destination'] else: labels['lblhostlog'] = "Local" boot_time = self.humanize_time(time.time() - int(host_other_config['boot_time'][:-1])) tool_boot_time = self.humanize_time(time.time() - int(host_other_config['agent_start_time'][:-1])) labels['lblhostuptime'] = boot_time labels['lblhosttooluptime'] = tool_boot_time labels['lblhostdns'] = self.all_hosts[ref]['hostname'] labels['lblhostprimary'] = self.all_hosts[ref]['address'] resident_vms = self.all_hosts[ref]['resident_VMs'] host_vms_memory = "" for resident_vm_uuid in resident_vms: if self.all_vms[resident_vm_uuid]['is_control_domain']: host_memory = self.all_vms[resident_vm_uuid]['memory_target'] else: host_vms_memory += self.all_vms[resident_vm_uuid]['name_label'] \ + ": using " + \ self.convert_bytes(self.all_vms[resident_vm_uuid]['memory_dynamic_max']) + "\n" host_metrics_uuid = self.all_hosts[ref]['metrics'] host_metrics = self.all_host_metrics[host_metrics_uuid] labels['lblhostmemserver'] = "%s free of %s available (%s total)" % \ (self.convert_bytes(host_metrics['memory_free']), \ self.convert_bytes(int(host_metrics['memory_total']) - int(host_memory)), \ self.convert_bytes(host_metrics['memory_total'])) labels['lblhostmemoryvms'] = host_vms_memory labels['lblhostmemory'] = self.convert_bytes(host_memory) labels['lblhostversiondate'] = software_version['date'] labels['lblhostversionbuildnumber'] = software_version['build_number'] labels['lblhostversionbuildversion'] = software_version['product_version'] expiry = self.humanize_time(self.get_seconds_difference_reverse(license_params['expiry'])) labels['lblhostlicexpire'] = expiry labels['lblhostlicserver'] = license_params['sku_marketing_name'] labels['lblhostliccode'] = license_params['productcode'] labels['lblhostlicserial'] = license_params['serialnumber'] host_cpus = self.all_hosts[ref]['host_CPUs'] cpus = "" for host_cpu_uuid in host_cpus: cpus += "Vendor: %s\nModel: %s\nSpeed: %s\n" % \ (self.all_host_cpu[host_cpu_uuid]['vendor'], self.all_host_cpu[host_cpu_uuid]['modelname'], self.all_host_cpu[host_cpu_uuid]['speed']) labels['lblhostcpus'] = cpus host_patchs = self.all_hosts[ref]['patches'] patchs = "" for host_cpu_patch in host_patchs: pool_patch = self.all_host_patch[host_cpu_patch]['pool_patch'] patchs += self.all_pool_patch[pool_patch]['name_label'] + "\n" labels['lblhostpatchs'] = patchs # TODO: list hotfix applied for label in labels.keys(): builder.get_object(label).set_label(labels[label]) def update_tab_pool_general(self, ref, builder): labels = {} if ref not in self.all_pools: return labels["lblpoolname"] = self.all_pools[ref]['name_label'] labels["lblpooldescription"] = self.all_pools[ref]['name_description'] other_config = self.all_pools[ref]['other_config'] if self.all_pools[ref]['tags']: labels["lblpooltags"] = ", ".join(self.all_pools[ref]['tags']) else: labels["lblpooltags"] = "" if "folder" in other_config: labels["lblpoolfolder"] = other_config['folder'] else: labels["lblpoolfolder"] = "" fullpatchs = "" partialpatchs = "" for patch in self.all_pool_patch: hosts = {} for host_patch in self.all_pool_patch[patch]["host_patches"]: host = self.all_host_patch[host_patch]["host"] if host not in hosts: hosts[host] = [] hosts[host] += self.all_pool_patch[patch]["host_patches"] if hosts.keys() == self.all_hosts.keys(): fullpatchs += self.all_pool_patch[patch]["name_label"] + "\n" else: partialpatchs += self.all_pool_patch[patch]["name_label"] + "\n" labels["lblpoolfullpatchs"] = fullpatchs labels["lblpoolpartialpatchs"] = partialpatchs for label in labels.keys(): builder.get_object(label).set_label(labels[label]) if partialpatchs == "": builder.get_object("label365").hide() builder.get_object("lblpoolpartialpatchs").hide() if fullpatchs == "": builder.get_object("label363").hide() builder.get_object("lblpoolfullpatchs").hide() def update_tab_vm_general(self, ref, builder): self.builder = builder labels = {} if ref in self.all_vms: metric = self.all_vms[ref]['metrics'] metric_guest = self.all_vms[ref]['guest_metrics'] labels["lblvmname"] = self.all_vms[ref]['name_label'] labels["lblvmdescription"] = self.all_vms[ref]['name_description'] labels["lblvmuuid"] = self.all_vms[ref]['uuid'] labels["lblvmmemory"] = self.convert_bytes(self.all_vms[ref]['memory_dynamic_max']) if self.all_vms[ref]['tags']: labels["lblvmtags"] = ", ".join(self.all_vms[ref]['tags']) else: labels["lblvmtags"] = "" labels["lblvmcpu"] = self.all_vms[ref]['VCPUs_at_startup'] other_config = self.all_vms[ref]['other_config'] if "auto_poweron" in other_config and other_config["auto_poweron"] == "true": labels["lblvmautoboot"] = "Yes" else: labels["lblvmautoboot"] = "No" if not self.all_vms[ref]['HVM_boot_policy']: labels['lblvmboot'] = "Boot order:" labels["lblvmparameters"] = self.all_vms[ref]['PV_args'] else: labels['lblvmboot'] = "OS boot parameters:" labels['lblvmparameters'] = "" for param in list(self.all_vms[ref]['HVM_boot_params']['order']): if param == 'c': labels['lblvmparameters'] += "Hard Disk\n" elif param == 'd': labels['lblvmparameters'] += "DVD-Drive\n" elif param == 'n': labels['lblvmparameters'] += "Network\n" priority = self.all_vms[ref]["VCPUs_params"] if "weight" in priority: weight = int(priority['weight']) if weight == 1: labels["lblvmpriority"] = "Lowest" elif weight <= 4: labels["lblvmpriority"] = "Very Low" elif weight <= 32: labels["lblvmpriority"] = "Low" elif weight <= 129: labels["lblvmpriority"] = "Below Normal" elif weight <= 512: labels["lblvmpriority"] = "Normal" elif weight <= 2048: labels["lblvmpriority"] = "Above Normal" elif weight <= 4096: labels["lblvmpriority"] = "High" elif weight <= 16384: labels["lblvmpriority"] = "Very High" else: labels["lblvmpriority"] = "Highest" else: labels["lblvmpriority"] = "Normal" #FIXME #labels["lblvmstartup"] = str(self.connection.VM_metrics.get_start_time(self.session_uuid,metric)['Value']) metric = self.all_vms[ref]['metrics'] if metric not in self.all_vm_metrics: res = self.connection.VM_metrics.get_record(self.session_uuid, ref) if "Value" in res: self.all_vm_metrics[ref] = res["Value"] if metric in self.all_vm_metrics: if self.all_vm_metrics[metric]['start_time'] != "19700101T00:00:00Z": startup = self.humanize_time(self.get_seconds_difference(self.all_vm_metrics[metric]['start_time'])) labels["lblvmstartup"] = startup else: labels["lblvmstartup"] = "never started up" else: labels["lblvmstartup"] = "" labels['lblvmdistro'] = "" if metric_guest != "OpaqueRef:NULL" and metric_guest in self.all_vm_guest_metrics: guest_metrics = self.all_vm_guest_metrics[metric_guest] if "PV_drivers_up_to_date" in guest_metrics and guest_metrics['PV_drivers_up_to_date']: state = "Optimized" else: state = "Not optimized" if "PV_drivers_up_to_date" in guest_metrics and "major" in guest_metrics["PV_drivers_version"]: if "build" in guest_metrics['PV_drivers_version']: state = state + " (version " + guest_metrics['PV_drivers_version']['major'] + "."\ + guest_metrics['PV_drivers_version']['minor'] + " build "\ + guest_metrics['PV_drivers_version']['build'] + ")" else: state = state + " (version " + guest_metrics['PV_drivers_version']['major'] + "."\ + guest_metrics['PV_drivers_version']['minor'] + " build )" else: state = "Tools not installed" labels["lblvmvirtstate"] = state if "name" in guest_metrics["os_version"]: labels["lblvmdistro"] = guest_metrics["os_version"]["name"] else: state = "Tools not installed" labels["lblvmvirtstate"] = state if "folder" in other_config: labels["lblvmfolder"] = other_config['folder'] else: labels["lblvmfolder"] = "" for label in labels.keys(): builder.get_object(label).set_label(labels[label]) def export_vm(self, ref, destination, ref2=None, as_vm = False): if ref2: task_uuid = self.connection.task.create(self.session_uuid, "Exporting snapshot", "Exporting snapshot " + destination) else: task_uuid = self.connection.task.create(self.session_uuid, "Exporting VM", "Exporting VM " + destination) self.track_tasks[task_uuid['Value']] = ref2 if ref2 else ref url = "http://%s/export?ref=%s&session_id=%s&task_id=%s" % (self.wine.selected_host, ref, self.session_uuid, task_uuid['Value']) Thread(target=self.download_export, args=(url,destination, ref, as_vm)).start() def download_export(self, url, destination, ref, as_vm): #print "Saving %s to %s" % (url, destination) if as_vm: self.connection.VM.set_is_a_template(self.session_uuid, ref, False) urllib.urlretrieve(url, destination) if as_vm: self.connection.VM.set_is_a_template(self.session_uuid, ref, True) def get_actions(self, ref): return self.all_vms[ref]['allowed_operations'] def get_connect_string(self, ref): #FIXME """ vm_uuid = self.connection.VM.get_by_uuid(self.session_uuid,uuid) consoles = self.connection.VM.get_consoles(self.session_uuid, vm_uuid['Value']) console = self.connection.console.get_record(self.session_uuid,consoles['Value'][0]) """ return "CONNECT /console?ref=%s&session_id=%s HTTP/1.1\r\n\r\n" % (ref,self.session_uuid) def get_connect_parameters(self, ref, host): """ vm_uuid = self.connection.VM.get_by_uuid(self.session_uuid,uuid) consoles = self.connection.VM.get_consoles(self.session_uuid, vm_uuid['Value']) console = self.connection.console.get_record(self.session_uuid,consoles['Value'][0]) """ return "%s %s %s" % (host, ref, self.session_uuid) # TODO: these should *not* be here # { def dump(self, obj): for attr in dir(obj): print "obj.%s = %s" % (attr, getattr(obj, attr)) def humanize_time(self, secs): string = "" mins, secs = divmod(secs, 60) hours, mins = divmod(mins, 60) days, hours = divmod(hours, 24) if days: string += "%02d days " % (days) if hours: string += "%02d hours " % (hours) if mins: string += "%02d minutes " % (mins) if secs: string += "%02d seconds " % (secs) return string def convert_bytes(self, n): """ http://www.5dollarwhitebox.org/drupal/node/84 """ n = float(n) K, M, G, T = 1 << 10, 1 << 20, 1 << 30, 1 << 40 if n >= T: return '%.2fT' % (float(n) / T) elif n >= G: return '%.2fG' % (float(n) / G) elif n >= M: return '%.2fM' % (float(n) / M) elif n >= K: return '%.2fK' % (float(n) / K) else: return '%d' % n # } def thread_host_search(self, ref, list): Thread(target=self.fill_host_search, args=(ref, list)).start() return True def search_ref(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 6) == user_data: self.found_iter = iter_ref def event_next(self): print "Entering event loop" while not self.halt: try: eventn = self.connection_events.event.next(self.session_events_uuid) if "Value" in eventn: for event in eventn["Value"]: if event['class'] == "vm": if event['operation'] == "add": self.all_vms[event["ref"]] = event['snapshot'] if not self.all_vms[event["ref"]]["is_a_snapshot"]: gobject.idle_add(lambda: self.add_vm_to_tree(event["ref"]) and False) else: gobject.idle_add(lambda: self.fill_vm_snapshots(self.wine.selected_ref, \ self.wine.builder.get_object("treevmsnapshots"), \ self.wine.builder.get_object("listvmsnapshots")) and False) gobject.idle_add(lambda: self.wine.modelfilter.clear_cache() and False) gobject.idle_add(lambda: self.wine.modelfilter.refilter() and False) for track in self.track_tasks: if self.track_tasks[track] == "Import.VM": self.track_tasks[track] = event["ref"] if self.track_tasks[track] == "Backup.Server": self.track_tasks[track] = event["ref"] if self.track_tasks[track] == "Restore.Server": self.track_tasks[track] = event["ref"] if self.track_tasks[track] == "Backup.Pool": self.track_tasks[track] = event["ref"] if self.track_tasks[track] == "Restore.Pool": self.track_tasks[track] = event["ref"] if self.track_tasks[track] == "Upload.Patch": self.track_tasks[track] = event["ref"] self.wine.builder.get_object("wprogressimportvm").hide() # Perfect -> set now import_ref to event["ref"] self.import_ref = event["ref"] elif event['operation'] == "del": if not self.all_vms[event["ref"]]["is_a_snapshot"]: self.found_iter = None self.treestore.foreach(self.search_ref, event["ref"]) if self.found_iter: gobject.idle_add(lambda: self.treestore.remove(self.found_iter) and False) del self.all_vms[event["ref"]] else: gobject.idle_add(lambda: self.fill_vm_snapshots(self.wine.selected_ref, \ self.wine.builder.get_object("treevmsnapshots"), \ self.wine.builder.get_object("listvmsnapshots")) and False) del self.all_vms[event["ref"]] else: self.filter_uuid = event['snapshot']['uuid'] if self.vm_filter_uuid(): #make into a template if event['snapshot']['is_a_template'] != self.all_vms[self.vm_filter_uuid()]['is_a_template']: self.all_vms[self.vm_filter_uuid()] = event['snapshot'] self.found_iter = None self.treestore.foreach(self.search_ref, event["ref"]) if self.found_iter and event['snapshot']['is_a_template']: gobject.idle_add(lambda: self.treestore.set(self.found_iter, 0, gtk.gdk.pixbuf_new_from_file("images/user_template_16.png"), 3, "custom_template") and False) gobject.idle_add(lambda: self.wine.update_tabs() and False) else: if event['snapshot']['resident_on'] != self.all_vms[self.vm_filter_uuid()]['resident_on']: self.found_iter = None gobject.idle_add(lambda: self.treestore.foreach(self.search_ref, event["ref"]) and False) if self.found_iter: gobject.idle_add(lambda: self.treestore.remove(self.found_iter) and False) self.all_vms[self.vm_filter_uuid()] = event['snapshot'] gobject.idle_add(lambda: self.add_vm_to_tree(event["ref"] and False)) if event['snapshot']['affinity'] != self.all_vms[self.vm_filter_uuid()]['affinity']: print "migrate or start on or resume on2" self.all_vms[self.vm_filter_uuid()] = event['snapshot'] else: if event["ref"] in self.track_tasks: self.all_vms[self.track_tasks[event["ref"]]] = event['snapshot'] else: self.all_vms[event["ref"]] = event['snapshot'] self.all_vms[event["ref"]] = event['snapshot'] self.treestore.foreach(self.update_vm_status, "") gobject.idle_add(lambda: self.wine.update_memory_tab() and False) elif event['class'] == "vm_guest_metrics": self.all_vm_guest_metrics[event['ref']] = self.connection.VM_guest_metrics.get_record(self.session_uuid, event['ref']) elif event['class'] == "task": #print ">>>" + event["snapshot"]["name_label"] + " " + event["snapshot"]["status"] + " " + str(event["snapshot"]["progress"]) + ":\t", event self.all_tasks[event["ref"]] = event["snapshot"] if event["ref"] not in self.track_tasks: #print event #print event["snapshot"]["name_label"] + " " + event["snapshot"]["status"] + " " + str(event["snapshot"]["progress"]) + ":\t", event pass if event["snapshot"]["status"] == "success": if event["ref"] in self.vboxchildprogressbar: self.vboxchildprogress[event["ref"]].hide() self.vboxchildprogressbar[event["ref"]].hide() self.vboxchildcancel[event["ref"]].hide() if event["snapshot"]["error_info"]: if event["ref"] in self.track_tasks: if self.track_tasks[event["ref"]] in self.all_vms: gobject.idle_add(lambda: self.wine.push_error_alert("%s %s %s" % (event["snapshot"]["name_label"], self.all_vms[self.track_tasks[event["ref"]]]["name_label"], event["snapshot"]["error_info"])) and False) eref = event["ref"] if eref in self.vboxchildcancel: self.vboxchildcancel[eref].hide() self.vboxchildprogressbar[eref].hide() self.vboxchildprogress[eref].set_label(str(event["snapshot"]["error_info"])) self.vboxchildprogress[eref].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FF0000')) else: self.wine.builder.get_object("wprogressimportvm").hide() self.wine.builder.get_object("tabboximport").set_current_page(2) gobject.idle_add(lambda: self.wine.push_error_alert("%s: %s" % (event["snapshot"]["name_description"], event["snapshot"]["error_info"])) and False) else: if event["ref"] in self.track_tasks: if self.track_tasks[event["ref"]] in self.all_vms: if event["snapshot"]["status"] == "success": gobject.idle_add(lambda: self.wine.push_alert("%s %s completed" % (event["snapshot"]["name_label"], self.all_vms[self.track_tasks[event["ref"]]]["name_label"])) and False) else: gobject.idle_add(lambda: self.wine.push_alert("%s %s %s" % (event["snapshot"]["name_label"], self.all_vms[self.track_tasks[event["ref"]]]["name_label"], (" %.2f%%" % (float(event["snapshot"]["progress"])*100)))) and False) else: vm = self.connection.VM.get_record(self.session_uuid, self.track_tasks[event["ref"]]) if "Value" in vm: self.all_vms[self.track_tasks[event["ref"]]] = vm['Value'] #self.add_vm_to_tree(self.track_tasks[event["ref"]]) gobject.idle_add(lambda: self.wine.modelfilter.clear_cache() and False) gobject.idle_add(lambda: self.wine.modelfilter.refilter() and False) gobject.idle_add(lambda: self.wine.push_alert("%s %s %s" % (event["snapshot"]["name_label"], self.all_vms[self.track_tasks[event["ref"]]]["name_label"], (" %.2f%%" % (float(event["snapshot"]["progress"])*100)))) and False) else: gobject.idle_add(lambda: self.wine.push_alert("%s: %s %s" % (event["snapshot"]["name_label"], event["snapshot"]["name_description"], (" %.2f%%" % (float(event["snapshot"]["progress"])*100)))) and False) else: pass #FIXME? #self.wine.push_alert(event["snapshot"]["name_label"] + (" %.2f%%" % (float(event["snapshot"]["progress"])*100))) if event["snapshot"]["status"] == "success": if event["snapshot"]["name_label"] == "Async.VIF.create": dom = xml.dom.minidom.parseString(event['snapshot']['result']) nodes = dom.getElementsByTagName("value") vif_ref = nodes[0].childNodes[0].data self.connection.VIF.plug(self.session_uuid, vif_ref) if self.wine.selected_tab == "VM_Network": gobject.idle_add(lambda: self.fill_vm_network(self.wine.selected_ref, self.wine.builder.get_object("treevmnetwork"), self.wine.builder.get_object("listvmnetwork")) and False) if event["snapshot"]["name_label"] == "Async.VM.revert": self.start_vm(self.track_tasks[event["ref"]]) if event["snapshot"]["name_label"] in ("Async.VM.clone", "Async.VM.copy"): dom = xml.dom.minidom.parseString(event['snapshot']['result']) nodes = dom.getElementsByTagName("value") vm_ref = nodes[0].childNodes[0].data #self.add_vm_to_tree(vm_ref) if event["ref"] in self.set_descriptions: self.connection.VM.set_name_description(self.session_uuid, vm_ref, self.set_descriptions[event["ref"]]) if event["snapshot"]["name_label"] in ("Async.VM.provision", "Async.VM.clone", "Async.VM.copy"): self.filter_uuid = event['snapshot']['uuid'] # TODO # Detect VM with event["ref"] if event["ref"] in self.track_tasks and self.track_tasks[event["ref"]] in self.all_vms: for vbd in self.all_vms[self.track_tasks[event["ref"]]]['VBDs']: self.all_storage[vbd] = self.connection.VBD.get_record(self.session_uuid, vbd)['Value'] for vif in self.all_vms[self.track_tasks[event["ref"]]]['VIFs']: self.all_vif[vif] = self.connection.VIF.get_record(self.session_uuid, vif)['Value'] if self.vm_filter_uuid() != None: self.all_vms[self.vm_filter_uuid()]['allowed_operations'] = \ self.connection.VM.get_allowed_operations(self.session_uuid, self.vm_filter_uuid())['Value'] else: if event["ref"] in self.track_tasks: self.all_vms[self.track_tasks[event["ref"]]]['allowed_operations'] = \ self.connection.VM.get_allowed_operations(self.session_uuid, self.track_tasks[event["ref"]])['Value'] if self.all_vms[self.track_tasks[event["ref"]]]['allowed_operations'].count("start"): if self.track_tasks[event["ref"]] in self.autostart: host_start = self.autostart[self.track_tasks[event["ref"]]] res = self.connection.Async.VM.start_on(self.session_uuid, self.track_tasks[event["ref"]], host_start, False, False) if "Value" in res: self.track_tasks[res['Value']] = self.track_tasks[event["ref"]] else: print res if event["snapshot"]["name_label"] == "Async.VM.snapshot": self.filter_uuid = event['snapshot']['uuid'] if self.track_tasks[event["ref"]] in self.all_vms: vm_uuid = self.track_tasks[event["ref"]] dom = xml.dom.minidom.parseString(event['snapshot']['result']) nodes = dom.getElementsByTagName("value") snapshot_ref = nodes[0].childNodes[0].data #self.all_vms[vm_uuid]['snapshots'].append(snapshot_ref) self.all_vms[snapshot_ref] = self.connection.VM.get_record(self.session_uuid, snapshot_ref)['Value'] for vbd in self.all_vms[snapshot_ref]['VBDs']: #FIXME self.all_vbd[vbd] = self.connection.VBD.get_record(self.session_uuid, vbd)['Value'] if self.track_tasks[event["ref"]] == self.wine.selected_ref and \ self.wine.selected_tab == "VM_Snapshots": gobject.idle_add(lambda: self.fill_vm_snapshots(self.wine.selected_ref, \ self.wine.builder.get_object("treevmsnapshots"), \ self.wine.builder.get_object("listvmsnapshots")) and False) if event["snapshot"]["name_label"] == "VM.Async.snapshot": if self.track_tasks[event["ref"]] == self.wine.selected_ref and \ self.wine.selected_tab == "VM_Snapshots": gobject.idle_add(lambda: self.fill_vm_snapshots(self.wine.selected_ref, \ self.wine.builder.get_object("treevmsnapshots"), \ self.wine.builder.get_object("listvmsnapshots")) and False) if event["snapshot"]["name_label"] == "Importing VM": if self.import_start: self.start_vm(self.track_tasks[event["ref"]]) if self.import_make_into_template: self.make_into_template(self.track_tasks[event["ref"]]) if event["snapshot"]["name_label"] == "VM.destroy": if self.wine.selected_tab == "VM_Snapshots": gobject.idle_add(lambda: self.fill_vm_snapshots(self.wine.selected_ref, \ self.wine.builder.get_object("treevmsnapshots"), \ self.wine.builder.get_object("listvmsnapshots")) and False) if event["snapshot"]["name_label"] == "VIF.destroy": if self.wine.selected_tab == "VM_Network": gobject.idle_add(lambda: self.fill_vm_network(self.wine.selected_ref, \ self.wine.builder.get_object("treevmnetwork"), \ self.wine.builder.get_object("listvmnetwork")) and False) if event["snapshot"]["name_label"] == "VIF.plug": if self.wine.selected_tab == "VM_Network": gobject.idle_add(lambda: self.fill_vm_network(self.wine.selected_ref, \ self.wine.builder.get_object("treevmnetwork"), \ self.wine.builder.get_object("listvmnetwork")) and False) if event["snapshot"]["name_label"] in ("VBD.create", "VBD.destroy"): if self.wine.selected_tab == "VM_Storage": #print "fill_vm_storage start" gobject.idle_add(lambda: self.fill_vm_storage(self.wine.selected_ref, \ self.wine.builder.get_object("listvmstorage")) and False) #print pdb.set_trace() #print "fill_vm_storage end" if event["snapshot"]["name_label"] in ("VDI.create", "VDI.destroy"): if self.wine.selected_tab == "Local_Storage": gobject.idle_add(lambda: self.fill_local_storage(self.wine.selected_ref, \ self.wine.builder.get_object("liststg")) and False) if event["snapshot"]["name_label"] in ("network.create", "network.destroy"): if self.wine.selected_tab == "HOST_Network": gobject.idle_add(lambda: self.wine.update_tab_host_network() and False) if event["snapshot"]["name_label"] in ("Async.Bond.create", "Bond.create", "Async.Bond.destroy", "Bond.destroy"): if self.wine.selected_tab == "HOST_Nics": gobject.idle_add(lambda: self.wine.update_tab_host_nics() and False) if event["ref"] in self.track_tasks: self.tasks[event["ref"]] = event if event["ref"] in self.vboxchildprogressbar: self.vboxchildprogressbar[event["ref"]].set_fraction(float(event["snapshot"]["progress"])) else: if event["ref"] in self.track_tasks: self.tasks[event["ref"]] = event if self.track_tasks[event["ref"]] == self.wine.selected_ref and \ self.wine.selected_tab == "VM_Logs": if event["ref"] in self.track_tasks and event["ref"] not in self.vboxchildprogressbar: gobject.idle_add(lambda: self.fill_vm_log(self.wine.selected_uuid, thread=True) and False) else: if event["snapshot"]["name_label"] == "Exporting VM" and event["ref"] not in self.vboxchildprogressbar: self.track_tasks[event["ref"]] = self.wine.selected_ref self.tasks[event["ref"]] = event gobject.idle_add(lambda: self.fill_vm_log(self.wine.selected_uuid, thread=True) and False) else: #print event pass elif event["class"] == "vdi": self.all_vdi[event["ref"]] = event["snapshot"] if self.wine.selected_tab == "Local_Storage": liststg = self.wine.builder.get_object("liststg") gobject.idle_add(lambda: self.fill_local_storage(self.wine.selected_ref,liststg) and False) if self.wine.selected_tab == "VM_Storage": gobject.idle_add(lambda: self.fill_vm_storage(self.wine.selected_ref, \ self.wine.builder.get_object("listvmstorage")) and False) elif event["class"] == "vbd": self.all_vbd[event["ref"]] = event["snapshot"] """ if event["snapshot"]["allowed_operations"].count("attach") == 1: self.last_vbd = event["ref"] """ elif event["class"] == "pif": self.all_pif[event["ref"]] = event["snapshot"] if self.wine.selected_tab == "HOST_Nics": gobject.idle_add(lambda: self.wine.update_tab_host_nics() and False) elif event["class"] == "bond": if event["operation"] == "del": del self.all_bond[event["ref"]] else: self.all_bond[event["ref"]] = event["snapshot"] if self.wine.selected_tab == "HOST_Nics": gobject.idle_add(lambda: self.wine.update_tab_host_nics() and False) elif event["class"] == "vif": if event["operation"] == "del": del self.all_vif[event["ref"]] else: if event["operation"] == "add": self.connection.VIF.plug(self.session_uuid, event["ref"]) self.all_vif[event["ref"]] = event["snapshot"] elif event["class"] == "sr": self.filter_uuid = event['snapshot']['uuid'] self.all_storage[event["ref"]] = event["snapshot"] self.treestore.foreach(self.update_storage_status, "") if event["operation"] == "del": self.filter_uuid = event['snapshot']['uuid'] gobject.idle_add(lambda: self.treestore.foreach(self.delete_storage, "") and False) if event["operation"] == "add": sr = event["ref"] # FIXME host = self.all_hosts.keys()[0] if self.poolroot: #iter_ref = self.treestore.append(self.poolroot, [\ gobject.idle_add(lambda: self.treestore.append(self.poolroot, [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ self.all_storage[sr]['name_label'], self.all_storage[sr]['uuid'],\ "storage", None, self.host, sr, self.all_storage[sr]['allowed_operations'], None]) and False) else: #iter_ref = self.treestore.append(self.hostroot[host], [\ gobject.idle_add(lambda: self.treestore.append(self.hostroot[host], [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ self.all_storage[sr]['name_label'], self.all_storage[sr]['uuid'],\ "storage", None, self.host, sr, self.all_storage[sr]['allowed_operations'], None]) and False) elif event["class"] == "pool": if self.all_pools[event["ref"]]['name_label'] != event["snapshot"]["name_label"]: if self.poolroot: gobject.idle_add(lambda: self.wine.treestore.remove(self.poolroot) and False) else: for host_ref in self.hostroot.keys(): gobject.idle_add(lambda: self.wine.treestore.remove(self.hostroot[host_ref]) and False) self.sync() if self.all_pools[event["ref"]]['default_SR'] != event["snapshot"]["default_SR"]: self.treestore.foreach(self.update_default_sr, \ [self.all_pools[event["ref"]]['default_SR'], event["snapshot"]["default_SR"]]) self.all_pools[event["ref"]] = event["snapshot"] if self.wine.selected_type == "pool": self.update_tab_pool_general(self.wine.selected_ref, self.wine.builder) elif event["class"] == "message": if event["operation"] == "del": del self.all_messages[event["ref"]] elif event["operation"] == "add": self.all_messages[event["ref"]] = event["snapshot"] self.add_alert(event["snapshot"], event["ref"], self.wine.listalerts) self.wine.update_n_alerts() else: print event elif event["class"] == "vm_guest_metrics": self.all_vm_guest_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "network": if event["operation"] == "del": del self.all_network[event["ref"]] else: self.all_network[event["ref"]] = event["snapshot"] if self.wine.selected_tab == "HOST_Network": gobject.idle_add(lambda: self.wine.update_tab_host_network() and False) elif event["class"] == "vlan": if event["operation"] == "del": if event["ref"] in self.all_vlan: del self.all_vlan[event["ref"]] self.all_vlan[event["ref"]] = event["snapshot"] elif event["class"] == "host": if event["operation"] == "del": self.filter_uuid = event['snapshot']['uuid'] self.treestore.foreach(self.delete_host, "") del self.all_hosts[event["ref"]] elif event["operation"] == "add": self.all_hosts[event["ref"]] = event["snapshot"] self.wine.show_error_dlg("Host added, please reconnect for sync all info") else: self.filter_uuid = event['snapshot']['uuid'] self.all_hosts[event["ref"]] = event["snapshot"] self.treestore.foreach(self.update_host_status, "") elif event["class"] == "pif_metrics": self.all_pif_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "host_metrics": self.all_host_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "vbd_metrics": self.all_vbd_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "vif_metrics": self.all_vif_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "vm_metrics": self.all_vm_metrics[event["ref"]] = event["snapshot"] elif event["class"] == "console": self.all_console[event["ref"]] = event["snapshot"] elif event["class"] == "host_patch": if event["operation"] == "del": del self.all_host_patch[event["ref"]] else: self.all_host_patch[event["ref"]] = event["snapshot"] elif event["class"] == "pool_patch": if event["operation"] == "del": del self.all_pool_patch[event["ref"]] else: self.all_pool_patch[event["ref"]] = event["snapshot"] elif event["class"] == "pbd": self.all_pbd[event["ref"]] = event["snapshot"] if event["operation"] == "add": sr = event["snapshot"]["SR"] host = event["snapshot"]["host"] gobject.idle_add(lambda: self.treestore.insert_after(self.hostroot[host], self.last_storage_iter, [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ self.all_storage[sr]['name_label'], self.all_storage[sr]['uuid'],\ "storage", None, self.host, sr, self.all_storage[sr]['allowed_operations'], None]) and False) elif event["class"] == "host_cpu": self.all_host_cpu[event["ref"]] = event["snapshot"] else: print event["class"] + " => ",event except socket, msg: self.halt = True # FIXME TODO # Disconnect except httplib.CannotSendRequest: # TODO: csun: this is a common error/complaint. Find out why this is happening and fix this? print "Event loop received CannotSendRequest exception, retrying..." time.sleep(0.1) except: print "Event loop -- unexpected error:" print traceback.print_exc() print "Exiting event loop" def update_default_sr(self, model, path, iter_ref, user_data): """ user_data contains: [0] -> old default sr [1] -> new default sr """ sr = self.treestore.get_value(iter_ref, 6) if sr == user_data[0]: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png")) and False) if sr == user_data[1]: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png")) and False) self.default_sr = sr if sr == user_data[0] or sr == user_data[1]: if len(self.all_storage[sr]['PBDs']) == 0: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_detached_16.png")) and False) broken = False for pbd_ref in self.all_storage[sr]['PBDs']: if not self.all_pbd[pbd_ref]['currently_attached']: broken = True gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png")) and False) if not broken: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png")) and False) def update_vm_status(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 2) == self.filter_uuid: vm = self.all_vms[self.vm_filter_uuid()] if not self.all_vms[self.vm_filter_uuid()]["is_a_template"]: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 1, \ vm['name_label']) and False) if len(self.all_vms[self.vm_filter_uuid()]["current_operations"]): gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, \ gtk.gdk.pixbuf_new_from_file("images/tree_starting_16.png")) and False) else: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, \ gtk.gdk.pixbuf_new_from_file("images/tree_%s_16.png" % \ vm['power_state'].lower())) and False) gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 4, \ vm['power_state']) and False) self.wine.selected_state = vm['power_state'] self.wine.selected_actions = vm['allowed_operations'] else: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 1, \ vm['name_label']) and False) if self.wine.selected_ref == self.treestore.get_value(iter_ref, 6): gobject.idle_add(lambda: self.wine.update_tabs() and False) gobject.idle_add(lambda: self.wine.builder.get_object("headimage").set_from_pixbuf(self.treestore.get_value(iter_ref, 0)) and False) gobject.idle_add(lambda: self.wine.builder.get_object("headlabel").set_label(self.treestore.get_value(iter_ref, 1)) and False) def update_storage_status(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 2) == self.filter_uuid: storage = self.all_storage[self.storage_filter_uuid()] gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 1, \ storage['name_label'] ) and False) if self.wine.selected_ref == self.treestore.get_value(iter_ref, 6): gobject.idle_add(lambda: self.wine.update_tabs() and False) gobject.idle_add(lambda: self.wine.builder.get_object("headimage").set_from_pixbuf(self.treestore.get_value(iter_ref, 0)) and False) gobject.idle_add(lambda: self.wine.builder.get_object("headlabel").set_label(self.treestore.get_value(iter_ref, 1)) and False) sr = self.treestore.get_value(iter_ref, 6) if len(self.all_storage[sr]['PBDs']) == 0: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_detached_16.png")) and False) broken = False for pbd_ref in self.all_storage[sr]['PBDs']: if not self.all_pbd[pbd_ref]['currently_attached']: broken = True gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png")) and False) if not broken: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png")) and False) def delete_storage(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 2) == self.filter_uuid: self.treestore.remove(iter_ref) def update_host_status(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 2) == self.filter_uuid: if self.treestore.get_value(iter_ref, 1): host = self.all_hosts[self.host_filter_uuid()] gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 1, \ host['name_label'] ) and False) if host["enabled"]: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/tree_connected_16.png")) and False) else: gobject.idle_add(lambda: self.treestore.set_value(iter_ref, 0, gtk.gdk.pixbuf_new_from_file("images/tree_disabled_16.png")) and False) gobject.idle_add(lambda: self.wine.update_tabs() and False) gobject.idle_add(lambda: self.wine.update_toolbar() and False) gobject.idle_add(lambda: self.wine.update_menubar() and False) gobject.idle_add(lambda: self.wine.builder.get_object("headimage").set_from_pixbuf(self.treestore.get_value(iter_ref, 0)) and False) gobject.idle_add(lambda: self.wine.builder.get_object("headlabel").set_label(self.treestore.get_value(iter_ref, 1)) and False) def delete_host(self, model, path, iter_ref, user_data): if self.treestore.get_value(iter_ref, 2) == self.filter_uuid: gobject.idle_add(lambda: self.treestore.remove(iter_ref) and False) gobject.idle_add(lambda: self.wine.update_tabs() and False) def log_filter_uuid(self, item): return item["obj_uuid"] == self.filter_uuid def task_filter_uuid(self, item_ref): if item_ref in self.all_tasks: item = self.all_tasks[item_ref] if item_ref in self.track_tasks: if self.track_tasks[item_ref] in self.all_vms: return self.all_vms[self.track_tasks[item_ref]]["uuid"] == self.filter_uuid #return True if "ref" in item and item["ref"] in self.track_tasks and self.track_tasks[item["ref"]] in self.all_vms: return self.all_vms[self.track_tasks[item["ref"]]]["uuid"] == self.filter_uuid else: if "resident_on" in item: return item["resident_on"] == self.filter_ref if "uuid" in item: self.get_task_ref_by_uuid(item["uuid"]) return False def get_task_ref_by_uuid(self, uuid): for task in self.tasks.keys(): if "uuid" in self.tasks[task]: if uuid == self.tasks[task]["uuid"]: return task else: print self.tasks[task] def filter_vif_ref(self, item): return item["VM"] == self.filter_ref def filter_vbd_ref(self, item): return item["VM"] == self.filter_ref def filter_vbd_uuid(self, uuid): for vbd in self.all_vbd: if self.all_vbd[vbd]["uuid"] == uuid: return vbd return None def filter_vm_uuid(self, item): return item["uuid"] == self.filter_uuid def vm_filter_uuid(self): for vm in self.all_vms: if self.all_vms[vm]["uuid"] == self.filter_uuid: return vm return None def storage_filter_uuid(self): for stg in self.all_storage: if self.all_storage[stg]["uuid"] == self.filter_uuid: return stg return None def host_filter_uuid(self): for host in self.all_hosts: if self.all_hosts[host]["uuid"] == self.filter_uuid: return host return None def filter_custom_template(self, item): if not item["is_a_template"]: return False if item["name_label"][:7] == "__gui__": return False if item["last_booted_record"] != "": return True return False def filter_normal_template(self, item): if not item["is_a_template"]: return False elif item["name_label"][:7] == "__gui__": return False elif item["last_booted_record"] == "": return True return False def filter_vdi_ref(self): for vdi in self.all_vdi.keys(): if vdi == self.filter_vdi: return vdi def search_in_liststore(self, list, ref, field): """ Function retrns iter of element found or None """ print list.__len__() for i in range(0, list.__len__()): iter_ref = list.get_iter((i,)) print list.get_value(iter_ref, field) if ref == list.get_value(iter_ref, field): return iter_ref return None openxenmanager/window_host.py0000644000175000017500000006476611636446664015244 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python from window_host_nics import * from window_host_network import * from capabilities import capabilities_conf_text from threading import Thread from messages import messages_header import gtk class oxcWindowHost(oxcWindowHostNics, oxcWindowHostNetwork): """ Class to manage host tabs, host properties and host functions """ def on_btleavedomain_clicked(self, widget, data=None): """ Press "Leave Domain" on Users tab """ pass def on_btjoindomain_clicked(self, widget, data=None): """ Press "Join Domain" on Users tab """ pass def on_btadduser_clicked(self, widget, data=None): """ Press "Add user" on Users tab """ pass def on_btremoveuser_clicked(self, widget, data=None): """ Press "Remove user" on Users tab """ pass def on_btlogoutuser_clicked(self, widget, data=None): """ Press "Logout User" on Users tab """ pass def on_treeusers_cursor_changed(self, widget, data=None): """ Selected row in treeusers treeview """ pass def on_btchangerole_clicked(self, widget, data=None): """ Press "Join Domain" on Users tab """ pass def on_cancelfileexportmap_clicked(self, widget, data=None): """ Cancel dialog file export map to png """ self.builder.get_object("fileexportmap").hide() def on_acceptfileexportmap_clicked(self, widget, data=None): """ Accept dialog file export map to png """ filename = self.builder.get_object("fileexportmap").get_filename() pixbuf = gtk.gdk.Pixbuf( gtk.gdk.COLORSPACE_RGB, False, 8, 640, 480) pixmap = self.windowmap.widget.get_snapshot() pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) pixbuf.save(filename, 'png') self.builder.get_object("fileexportmap").hide() def on_btexportmap_clicked(self, widget, data=None): """ Function to export current map to PNG file """ self.builder.get_object("fileexportmap").set_current_name(self.selected_name + ".png") self.builder.get_object("fileexportmap").show() def on_check_map_options(self, widget, data=None): """ Function called when you check or uncheck a map option """ if not "maps" in self.config: self.config["maps"] = {} self.config["maps"][gtk.Buildable.get_name(widget)] = str(widget.get_active()) self.config.write() self.update_maps() def on_acceptfilenewupdate_clicked(self, widget, data=None): # When you press accept "new update" file chooser filename = self.builder.get_object("filenewupdate").get_filename() Thread(target=self.xc_servers[self.selected_host].upload_patch, \ args=(self.selected_ref, filename)).start() self.builder.get_object("updatemanager").hide() self.builder.get_object("filenewupdate").hide() def on_cancelfilenewupdate_clicked(self, widget, data=None): # When you press cancel "new update" file chooser self.builder.get_object("filenewupdate").hide() def on_btuploadnewupdate_clicked(self, widget, data=None): # When you press "Upload new update" (patch) self.builder.get_object("filterfilenewupdate").add_pattern("*.xsupdate") self.builder.get_object("filenewupdate").show() def on_btremoveupdate_clicked(self, wiget, data=None): # When you press "remove" (patch) treeupdates = self.builder.get_object("treeupdates") iter = treeupdates.get_selection().get_selected()[1] if iter: listupdates = self.builder.get_object("listupdates") patch_ref = listupdates.get_value(iter, 0) self.xc_servers[self.selected_host].remove_patch(self.selected_ref, patch_ref) self.builder.get_object("updatemanager").hide() def on_btapplypatch_clicked(self, widget, data=None): # When you press "apply patch" treeupdates = self.builder.get_object("treeupdates") treeupdatestatus = self.builder.get_object("treeupdatestatus") iter = treeupdates.get_selection().get_selected()[1] if iter: listupdates = self.builder.get_object("listupdates") patch_ref = listupdates.get_value(iter, 0) iter = treeupdatestatus.get_selection().get_selected()[1] if iter: listupdatestatus = self.builder.get_object("listupdatestatus") host_ref = listupdatestatus.get_value(iter, 0) self.xc_servers[self.selected_host].apply_patch(host_ref, patch_ref) self.builder.get_object("updatemanager").hide() def on_treeupdatestatus_cursor_changed(self, widget, data=None): # When you select a host in update manager iter = widget.get_selection().get_selected()[1] if iter: listupdatestatus = self.builder.get_object("listupdatestatus") self.builder.get_object("btapplypatch").set_sensitive(listupdatestatus.get_value(iter, 2)) def on_treeupdates_cursor_changed(self, widget, data=None): # When you select a patch iter = widget.get_selection().get_selected()[1] if iter: listupdates = self.builder.get_object("listupdates") ref = listupdates.get_value(iter, 0) name = self.xc_servers[self.selected_host].all_pool_patch[ref]['name_label'] desc = self.xc_servers[self.selected_host].all_pool_patch[ref]['name_description'] version = self.xc_servers[self.selected_host].all_pool_patch[ref]['version'] guidance = self.xc_servers[self.selected_host].all_pool_patch[ref]['after_apply_guidance'] self.builder.get_object("lblupdatename").set_label(name) self.builder.get_object("lblupdatedesc").set_label(desc) self.builder.get_object("lblupdateversion").set_label(version) guidance_text = "" for guid in guidance: if guid in messages_header: guidance_text += messages_header[guid] + "\n" else: guidance_text += guid self.builder.get_object("lblupdateguidance").set_label(guidance_text) host_patches = self.xc_servers[self.selected_host].all_pool_patch[ref]["host_patches"] self.builder.get_object("btremoveupdate").set_sensitive(len(host_patches) == 0) listupdatestatus = self.builder.get_object("listupdatestatus") listupdatestatus.clear() for host in self.xc_servers[self.selected_host].all_hosts.keys(): name = self.xc_servers[self.selected_host].all_hosts[host]['name_label'] found = False for host_patch in host_patches: host2 = self.xc_servers[self.selected_host].all_host_patch[host_patch]['host'] if host == host2: found = True timestamp = self.xc_servers[self.selected_host].all_host_patch[host_patch]['timestamp_applied'] patch_text = "%s - applied (%s)" % (name, \ self.xc_servers[self.selected_host].format_date(timestamp)) listupdatestatus.append([host, patch_text, False]) if not found: patch_text = "%s - not applied" % (name) listupdatestatus.append([host, patch_text, True]) def on_closeupdatemanager_clicked(self, widget, data=None): """ Function called when you close "update manager" window """ self.builder.get_object("updatemanager").hide() def on_txttemplatesearch_changed(self, widget, data=None): """ Function called when you type something on search template list (newvm) """ self.modelfiltertpl.refilter() def update_report_total_size_time(self): """ Update the total size and the total time on report status window """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") totalsize, totaltime = 0, 0 for i in range(0, listreport.__len__()): iter = listreport.get_iter((i,)) if listreport.get_value(iter, 1): totalsize += listreport.get_value(iter, 7) totaltime += listreport.get_value(iter, 8) self.builder.get_object("lblreportotalsize").set_label("< %s" % (self.convert_bytes(totalsize))) self.builder.get_object("lblreportotaltime").set_label("< %d minutes" % (int(totaltime)/60)) def on_cellrenderertoggle1_toggled(self, widget, data=None): """ Function called when you change the state of checkbox on report tree """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") iter = treereport.get_selection().get_selected()[1] if iter: listreport.set_value(iter, 1, not widget.get_active()) self.update_report_total_size_time() def on_treereport_cursor_changed(self, widget, data=None): """ Function called when you select a item on report """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") iter = treereport.get_selection().get_selected()[1] if iter: self.builder.get_object("lblreportdesc").set_label(listreport.get_value(iter, 4)) self.builder.get_object("lblreportsize").set_label(listreport.get_value(iter, 5)) self.builder.get_object("lblreporttime").set_label(listreport.get_value(iter, 6) + " seconds") conf = listreport.get_value(iter, 9) self.builder.get_object("lblreportconf").set_label(capabilities_conf_text[conf-1]) def on_acceptstatusreport_clicked(self, widget, data=None): """ Function called when you accept status report dialog """ from time import strftime self.builder.get_object("filesavereport").set_current_name(strftime("status-report-%Y-%m-%d-%H-%M-%S.tar")) self.builder.get_object("filesavereport").show() self.builder.get_object("statusreport").hide() def on_acceptfilereport_clicked(self, widget, data=None): """ Function called when you accept save report file chooser dialog """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") totalsize, totaltime = 0, 0 refs = [] for i in range(0, listreport.__len__()): iter = listreport.get_iter((i,)) if listreport.get_value(iter, 1): refs.append(listreport.get_value(iter, 0)) destination = self.builder.get_object("filesavereport").get_filename() Thread(target=self.xc_servers[self.selected_host].host_download_status_report, \ args=(self.selected_ref, ",".join(refs), destination, self.selected_name)).start() self.builder.get_object("filesavereport").hide() def on_cancelfilereport_clicked(self, widget, data=None): """ Function called when you cancel save report file chooser dialog """ self.builder.get_object("filesavereport").hide() def on_cancelstatusreport_clicked(self, widget, data=None): """ Function called when you cancel status report dialog """ self.builder.get_object("statusreport").hide() def on_clearallstatusreport_clicked(self, widget, data=None): """ Uncheck all checkbox for each status report """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") for i in range(0, listreport.__len__()): iter = listreport.get_iter((i,)) listreport.set_value(iter, 1, False) self.update_report_total_size_time() def on_selectallstatusreport_clicked(self, widget, data=None): """ Check all checkbox for each status report """ treereport = self.builder.get_object("treereport") listreport = self.builder.get_object("listreport") for i in range(0, listreport.__len__()): iter = listreport.get_iter((i,)) listreport.set_value(iter, 1, True) self.update_report_total_size_time() def on_txtcurrentpw_changed(self, widget, data=None): """ Function called when you change text on "current password", "type new password" or "re-enter new password" On change server password window """ # If some textfield is empty, then disable button if len(self.builder.get_object("txtcurrentpw").get_text()) and \ len(self.builder.get_object("txtnewpw").get_text()) and \ len(self.builder.get_object("txtrenewpw").get_text()) and \ (self.builder.get_object("txtnewpw").get_text() == self.builder.get_object("txtrenewpw").get_text()): self.builder.get_object("acceptchangepassword").set_sensitive(True) else: self.builder.get_object("acceptchangepassword").set_sensitive(False) def on_cancelchangepassword_clicked(self, widget, data=None): """ Function called when you press "Cancel" button on "Change Server Password" window """ self.builder.get_object("changepassword").hide() def on_acceptchangepassword_clicked(self, widget, data=None): """ Function called when you press "OK" button on "Change Server Password" window """ old = self.xc_servers[self.selected_host].password typed = self.builder.get_object("txtcurrentpw").get_text() if typed != old: self.builder.get_object("lblwrongpw").show() else: new = self.builder.get_object("txtnewpw").get_text() self.xc_servers[self.selected_host].change_server_password(old, new) self.builder.get_object("changepassword").hide() def update_tab_host_nics(self): """ Function called to fill host nics """ if self.treeview.get_cursor()[1]: listhostnics = self.builder.get_object("listhostnics") host = self.selected_host # Fill list "listhostnics" self.xc_servers[host].fill_host_nics(self.selected_ref, \ listhostnics) treehostnics = self.builder.get_object("treehostnics") # Select the first as default treehostnics.set_cursor((0,), treehostnics.get_column(0)) treehostnics.get_selection().select_path((0, )) iter = listhostnics.get_iter((0, )) # Get the reference of first selected ref = self.builder.get_object("listhostnics").get_value(iter, 8) nic_bond_master_of = self.xc_servers[self.selected_host].all_pif[ref]['bond_master_of'] # If is already on a bond if len(nic_bond_master_of): # Enable remove bond button self.builder.get_object("bthostnicremove").set_sensitive(True) else: # Disable remove bond button self.builder.get_object("bthostnicremove").set_sensitive(False) def update_tab_host_network(self): """ Function called to fill host networks """ if self.treeview.get_cursor()[1]: listhostnetwork= self.builder.get_object("listhostnetwork") host = self.selected_host # Fill list "listhostnetwork" self.xc_servers[host].fill_host_network(self.selected_ref, \ listhostnetwork) treehostnetwork = self.builder.get_object("treehostnetwork") # Select the first as default treehostnetwork.set_cursor((0,), treehostnetwork.get_column(0)) treehostnetwork.get_selection().select_path((0, 0)) iter = listhostnetwork.get_iter((0,0)) # Get the reference of first selected ref = self.builder.get_object("listhostnetwork").get_value(iter, 7) # Get the pifs from selected network network_pifs = self.xc_servers[self.selected_host].all_network[ref]['PIFs'] # Enable "remove network" by default self.builder.get_object("bthostnetworkremove").set_sensitive(True) for pif in network_pifs: # If is physical then disable it if self.xc_servers[self.selected_host].all_pif[pif]['physical'] == True: self.builder.get_object("bthostnetworkremove").set_sensitive(False) break def on_radiomgmtipmanual_toggled(self, widget, data=None): """ On "management interface" radio "manual ip" selected """ self.builder.get_object("txtmgmtip").set_sensitive(widget.get_active()) self.builder.get_object("txtmgmtmask").set_sensitive(widget.get_active()) self.builder.get_object("txtmgmtgw").set_sensitive(widget.get_active()) def on_radiomgmtdnsmanual_toggled(self, widget, data=None): """ On "management interface" radio "manual dns" selected """ self.builder.get_object("txtmgmtdns1").set_sensitive(widget.get_active()) self.builder.get_object("txtmgmtdns2").set_sensitive(widget.get_active()) def on_cancelmgmtinterface_clicked(self, widget, data=None): """ On "cancel" button pressed on "management interface" """ self.builder.get_object("mgmtinterface").hide() def on_checkpoolserver_toggled(self, widget, data=None): """ Function called on "new pool" window, when you check a server to join to pool """ listpoolvms = self.builder.get_object("listpoolvms") iter = listpoolvms.get_iter((int(data),)) # Field 4 (beginning on 0) contains if check could be modified if listpoolvms.get_value(iter, 4): # widget.get_active() contains last state: enabled o disabled listpoolvms.set(iter, 2, not widget.get_active()) def on_cancelnewpool_clicked(self, widget, data=None): """ On "cancel" button pressed on "new pool" """ self.builder.get_object("newpool").hide() def on_acceptnewpool_clicked(self, widget, data=None): """ On "accept" button pressed on "new pool" """ listpoolvms = self.builder.get_object("listpoolvms") listpoolmaster = self.builder.get_object("listpoolmaster") combopoolmaster = self.builder.get_object("combopoolmaster") # If a master pool is selected.. if combopoolmaster.get_active_iter(): name = self.builder.get_object("txtpoolname").get_text() desc = self.builder.get_object("txtpooldesc").get_text() # Get the reference the selected iter ref = listpoolmaster.get_value(combopoolmaster.get_active_iter(), 0) # Create a pool self.xc_servers[ref].create_pool(name, desc) # For each server on treeview for i in range(0, listpoolvms.__len__()): iter = listpoolvms.get_iter((int(i),)) # If is checked if listpoolvms.get_value(iter, 2): host = listpoolvms.get_value(iter, 0) # And is not "Master" if listpoolvms.get_value(iter, 3) == "": # Join to pool self.xc_servers[host].join_pool(self.xc_servers[ref].host, self.xc_servers[ref].user, self.xc_servers[ref].password) self.builder.get_object("newpool").hide() def on_combopoolmaster_changed(self, widget, data=None): # FIXME: active the selected on treeview and set as "Master" listpoolvms = self.builder.get_object("listpoolvms") listpoolmaster = self.builder.get_object("listpoolmaster") combopoolmaster = self.builder.get_object("combopoolmaster") if widget.get_active_iter(): ref = listpoolmaster.get_value(widget.get_active_iter(),0) for i in range(0, listpoolvms.__len__()): iter = listpoolvms.get_iter((int(i),)) if listpoolvms.get_value(iter, 3) == "Master": listpoolvms.set(iter, 3, "", 2, False, 4, True) if listpoolvms.get_value(iter, 0) == ref: listpoolvms.set(iter, 3, "Master", 2, True, 4, False) def on_canceldialogreconfigure_clicked(self, widget, data=None): """ On "cancel" button pressed on confirmation of "management interface" """ self.builder.get_object("dialogreconfigure").hide() def on_closewarninglicense_clicked(self, widget, data=None): """ On "close" button pressed on warning alert """ self.builder.get_object("warninglicense").hide() def on_accepthostdmesg_clicked(self, widget, data=None): """ On "accept" button pressed on dmesg dialog """ self.builder.get_object("hostdmesg").hide() def on_acceptdialogreconfigure_clicked(self, widget, data=None): """ On "accept" button pressed on confirmation dialog to reconfigure interface """ listmgmtinterfaces = self.builder.get_object("listmgmtinterfaces") treemgmtinterfaces = self.builder.get_object("treemgmtinterfaces") selection = treemgmtinterfaces.get_selection() pif_ref = listmgmtinterfaces.get_value(selection.get_selected()[1],0) combomgmtnetworks = self.builder.get_object("combomgmtnetworks") listmgmtnetworks = self.builder.get_object("listmgmtnetworks") iter = combomgmtnetworks.get_active_iter() # Get selected network and rest of elements network_ref = listmgmtnetworks.get_value(iter, 0) ip = self.builder.get_object("txtmgmtip").get_text() mask = self.builder.get_object("txtmgmtmask").get_text() gw = self.builder.get_object("txtmgmtgw").get_text() dns1 = self.builder.get_object("txtmgmtdns1").get_text() dns2 = self.builder.get_object("txtmgmtdns2").get_text() radiomgmtipdhcp = self.builder.get_object("radiomgmtipdhcp") radiomgmtdnsdhcp = self.builder.get_object("radiomgmtdnsdhcp") if radiomgmtdnsdhcp.get_active(): dns = "" else: dns = dns1 + "," + dns2 if radiomgmtipdhcp.get_active(): configuration_mode = "DHCP" else: configuration_mode = "Static" # Call to reconfigure interface with specified configuration self.xc_servers[self.selected_host].reconfigure_pif(pif_ref, configuration_mode, ip, mask, gw, dns, self.selected_ref) # Hide both windows: management window and confirmation self.builder.get_object("dialogreconfigure").hide() self.builder.get_object("mgmtinterface").hide() def on_acceptmgmtinterface_clicked(self, widget, data=None): """ On "accept" button pressed on confirmation dialog to reconfigure interface change is a variable, if is False doesn't change anything, if is True show reconfigure window confirmation """ listmgmtinterfaces = self.builder.get_object("listmgmtinterfaces") treemgmtinterfaces = self.builder.get_object("treemgmtinterfaces") # Get selected pif selection = treemgmtinterfaces.get_selection() pif_ref = listmgmtinterfaces.get_value(selection.get_selected()[1],0) combomgmtnetworks = self.builder.get_object("combomgmtnetworks") listmgmtnetworks = self.builder.get_object("listmgmtnetworks") # Get selected pif info pif = self.xc_servers[self.selected_host].all_pif[pif_ref] iter = combomgmtnetworks.get_active_iter() # Get selected network_ref pif = self.xc_servers[self.selected_host].all_pif[pif_ref] network_ref = listmgmtnetworks.get_value(iter, 0) if pif['network'] != network_ref: change = True radiomgmtipmanual = self.builder.get_object("radiomgmtipmanual") radiomgmtipdhcp = self.builder.get_object("radiomgmtipdhcp") radiomgmtdnsmanual = self.builder.get_object("radiomgmtdnsmanual") radiomgmtdnsdhcp = self.builder.get_object("radiomgmtdnsdhcp") ip = self.builder.get_object("txtmgmtip").get_text() mask = self.builder.get_object("txtmgmtmask").get_text() gw = self.builder.get_object("txtmgmtgw").get_text() dns1 = self.builder.get_object("txtmgmtdns1").get_text() dns2 = self.builder.get_object("txtmgmtdns2").get_text() change = False if pif['ip_configuration_mode'] == "DHCP" and radiomgmtipmanual.get_active(): change = True if pif['ip_configuration_mode'] != "DHCP" and radiomgmtipdhcp.get_active(): change = True if ip != pif['IP'] or mask != pif['netmask'] or gw != pif['gateway']: change = True if pif['DNS'] == "" and radiomgmtdnsmanual.get_active(): change = True if pif['DNS'] != "" and radiomgmtdnsdhcp.get_active(): change = True if dns1 + "," + dns2 != pif['DNS']: change = True # If some parameter was changed, show confirmation dialog, if not hide magement interface window if change: self.builder.get_object("dialogreconfigure").show() else: self.builder.get_object("mgmtinterface").hide() openxenmanager/plugins/0000755000175000017500000000000011636446664013764 5ustar rrsrrsopenxenmanager/plugins/GlpiWebUI.xcplugin.xml0000644000175000017500000000120211636446664020120 0ustar rrsrrs openxenmanager/plugins/OpenFilerWebUI.xcplugin.xml0000644000175000017500000000124311636446664021115 0ustar rrsrrs openxenmanager/plugins/NagiosWebUI.xcplugin.xml0000644000175000017500000000122711636446664020454 0ustar rrsrrs openxenmanager/rrdinfo.py0000755000175000017500000001600311636446664014323 0ustar rrsrrs#!/usr/bin/python # #Copyright (C) 2011 by Citrix Systems # #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: # # #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. # # Example code for reading RRDs # Contact: Jon Ludlam (jonathan.ludlam@eu.citrix.com) # # Mostly this script is taken from perfmon, by Alex Zeffert # import urllib from xml.dom import minidom from xml.parsers.expat import ExpatError import time # Per VM dictionary (used by RRDUpdates to look up column numbers by variable names) class VMReport(dict): """Used internally by RRDUpdates""" def __init__(self, uuid): self.uuid = uuid # Per Host dictionary (used by RRDUpdates to look up column numbers by variable names) class HostReport(dict): """Used internally by RRDUpdates""" def __init__(self, uuid): self.uuid = uuid class RRDUpdates: """ Object used to get and parse the output the http://localhost/rrd_udpates?... """ def __init__(self, url): self.url = url def get_nrows(self): return self.rows def get_vm_list(self): return self.vm_reports.keys() def get_vm_param_list(self, uuid): report = self.vm_reports[uuid] if not report: return [] return report.keys() def get_vm_data(self, uuid, param, row): report = self.vm_reports[uuid] col = report[param] return self.__lookup_data(col, row) def get_host_uuid(self): report = self.host_report if not report: return None return report.uuid def get_host_param_list(self): report = self.host_report if not report: return [] return report.keys() def get_host_data(self, param, row): report = self.host_report col = report[param] return self.__lookup_data(col, row) def get_row_time(self,row): return self.__lookup_timestamp(row) # extract float from value () node by col,row def __lookup_data(self, col, row): # Note: the nodes are in reverse chronological order, and comprise # a timestamp node, followed by self.columns data nodes if len(self.data_node.childNodes) >= (self.rows - 1 - row) and \ len(self.data_node.childNodes[self.rows - 1 - row].childNodes) > (col+1): node = self.data_node.childNodes[self.rows - 1 - row].childNodes[col+1] return float(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE return float(0) # extract int from value () node by row def __lookup_timestamp(self, row): # Note: the nodes are in reverse chronological order, and comprise # a timestamp node, followed by self.columns data nodes node = self.data_node.childNodes[self.rows - 1 - row].childNodes[0] return int(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE def refresh(self): sock = urllib.URLopener().open(self.url) xmlsource = sock.read() #sock.close() xmldoc = minidom.parseString(xmlsource) self.__parse_xmldoc(xmldoc) # Update the time used on the next run def __parse_xmldoc(self, xmldoc): # The 1st node contains meta data (description of the data) # The 2nd node contains the data self.meta_node = xmldoc.firstChild.childNodes[0] self.data_node = xmldoc.firstChild.childNodes[1] def lookup_metadata_bytag(name): return int (self.meta_node.getElementsByTagName(name)[0].firstChild.toxml()) # rows = number of samples per variable # columns = number of variables self.rows = lookup_metadata_bytag('rows') self.columns = lookup_metadata_bytag('columns') # These indicate the period covered by the data self.start_time = lookup_metadata_bytag('start') self.step_time = lookup_metadata_bytag('step') self.end_time = lookup_metadata_bytag('end') # the Node describes the variables self.legend = self.meta_node.getElementsByTagName('legend')[0] # vm_reports matches uuid to per VM report self.vm_reports = {} # There is just one host_report and its uuid should not change! self.host_report = None # Handle each column. (I.e. each variable) for col in range(self.columns): self.__handle_col(col) def __handle_col(self, col): # work out how to interpret col from the legend col_meta_data = self.legend.childNodes[col].firstChild.toxml() # vm_or_host will be 'vm' or 'host'. Note that the Control domain counts as a VM! (cf, vm_or_host, uuid, param) = col_meta_data.split(':') if vm_or_host == 'vm': # Create a report for this VM if it doesn't exist if uuid not in self.vm_reports: self.vm_reports[uuid] = VMReport(uuid) # Update the VMReport with the col data and meta data vm_report = self.vm_reports[uuid] vm_report[param] = col elif vm_or_host == 'host': # Create a report for the host if it doesn't exist if not self.host_report: self.host_report = HostReport(uuid) elif self.host_report.uuid != uuid: raise PerfMonException, "Host UUID changed: (was %s, is %s)" % (self.host_report.uuid, uuid) # Update the HostReport with the col data and meta data self.host_report[param] = col else: raise PerfMonException, "Invalid string in : %s" % col_meta_data def main(): rrd_updates = RRDUpdates() rrd_updates.refresh({}) for uuid in rrd_updates.get_vm_list(): print "Got values for VM: "+uuid for param in rrd_updates.get_vm_param_list(uuid): print "param: "+param data="" for row in range(rrd_updates.get_nrows()): data=data+"(%d,%f) " % (rrd_updates.get_row_time(row), rrd_updates.get_vm_data(uuid,param,row)) print data #main() openxenmanager/window_vm_network.py0000644000175000017500000003011211636446664016434 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import re class oxcWindowVMNetwork: """ Class to manage network of a VM """ def on_acceptdialognetworkrestart_clicked(self, widget, data=None): """ Function called when you accept "changes will take efect when you next restart the VM.." dialog """ self.builder.get_object("dialognetworkrestart").hide() def on_acceptremovenetwork_clicked(self, widget, data=None): """ Function called when you accept "remove network interface" confirmation dialog """ treenetwork = self.builder.get_object("treevmnetwork") listnetwork = self.builder.get_object("listvmnetwork") selection = treenetwork.get_selection() if selection.get_selected()[1] != None: iter = selection.get_selected()[1] self.xc_servers[self.selected_host].vm_remove_interface(self.selected_ref, listnetwork.get_value(iter,6)) self.builder.get_object("dialogremovenetwork").hide() def on_btaddinterface_clicked(self, widget, data=None): """ Function called when you click in "Add interface" button """ self.builder.get_object("dialogaddnetwork").show() treeaddnetwork = self.builder.get_object("treeaddnetwork") listaddnetwork = self.builder.get_object("listaddnetwork") self.xc_servers[self.selected_host].fill_addinterface_network(listaddnetwork) def on_cancelremovenetwork_clicked(self, widget, data=None): """ Function called when you cancel "remove network interface" confirmation dialog """ self.builder.get_object("dialogremovenetwork").hide() def on_btremoveinterface_clicked(self, widget, data=None): """ Function called when you click in "Remove interface" button on selected network """ # Show the configuration dialog self.builder.get_object("dialogremovenetwork").show() def on_btpropertiesinterface_clicked(self, widget, data=None): """ Function called when you click in "Properties" button on selected network """ treeeditnetwork = self.builder.get_object("treeeditnetwork") listeditnetwork = self.builder.get_object("listeditnetwork") treevmnetwork = self.builder.get_object("treevmnetwork") listvmnetwork = self.builder.get_object("listvmnetwork") selection = treevmnetwork.get_selection() # Network properties is a different dialog, is not a "properties" general dialog # If some network is selected if selection.get_selected()[1] != None: iter = selection.get_selected()[1] # Show the dialog self.builder.get_object("dialogeditnetwork").show() ref = listvmnetwork.get_value(iter, 6) network_ref = self.xc_servers[self.selected_host].all_vif[ref]['network'] # function "fill_editinterface_network" returns the position of selected network current = self.xc_servers[self.selected_host].fill_editinterface_network(listeditnetwork, network_ref) # Set the position in the combo treeeditnetwork.set_active(current) vif = self.xc_servers[self.selected_host].all_vif[ref] # Fill network information if "kbps" in vif['qos_algorithm_params']: self.builder.get_object("entryeditlimit").set_text(vif['qos_algorithm_params']["kbps"]) else: self.builder.get_object("entryeditlimit").set_text("") # If MAC is auto-generated if "MAC_autogenerated" in vif and vif['MAC_autogenerated']: # Enable radio "auto" and disable manual self.builder.get_object("radioeditauto").set_active(True) self.builder.get_object("radioeditmanual").set_active(False) self.builder.get_object("entryeditmac").set_text("") else: # Else disable radio "auto" and enable manual self.builder.get_object("radioeditmanual").set_active(True) self.builder.get_object("radioeditauto").set_active(False) self.builder.get_object("entryeditmac").set_text(vif['MAC']) def on_canceleditnetwork_clicked(self, widget, data=None): """ Function called when you cancel the edit network window """ self.builder.get_object("dialogeditnetwork").hide() def on_accepteditnetwork_clicked(self, widget, data=None): """ Function called when you accept the edit network window """ treeeditnetwork = self.builder.get_object("treeeditnetwork") listeditnetwork = self.builder.get_object("listeditnetwork") treevmnetwork = self.builder.get_object("treevmnetwork") listvmnetwork = self.builder.get_object("listvmnetwork") selection = treevmnetwork.get_selection() # Double check if network is selected if selection.get_selected()[1] != None: iter = selection.get_selected()[1] self.builder.get_object("dialogeditnetwork").show() ref = listvmnetwork.get_value(iter, 6) network_ref = self.xc_servers[self.selected_host].all_vif[ref]['network'] vif = self.xc_servers[self.selected_host].all_vif[ref] mac = self.builder.get_object("entryeditmac").get_text() limit = self.builder.get_object("entryeditlimit").get_text() # modify is a flag variable modify = False if "kbps" in vif['qos_algorithm_params']: if self.builder.get_object("entryeditlimit").get_text() != vif['qos_algorithm_params']["kbps"]: modify = True else: modify = True if "MAC_autogenerated" in vif and vif['MAC_autogenerated']: if self.builder.get_object("radioeditmanual").get_active(): modify = True self.xc_servers[self.selected_host].vm_add_interface(self.selected_ref, network_ref, mac, limit) else: if self.builder.get_object("radioeditauto").get_active(): mac = "" modify = True if vif['MAC'] != self.builder.get_object("entryeditmac").get_text(): modify = True if network_ref != listeditnetwork.get_value(treeeditnetwork.get_active_iter(),0): modify = True # if some element was modified.. if modify: # then remove interface self.xc_servers[self.selected_host].vm_remove_interface(self.selected_ref, ref) network_ref = listeditnetwork.get_value(treeeditnetwork.get_active_iter(),0) mac = self.builder.get_object("entryeditmac").get_text() limit = self.builder.get_object("entryeditlimit").get_text() # Add create again self.xc_servers[self.selected_host].vm_add_interface(self.selected_ref, network_ref, mac, limit) # And show dialog saying changes will be applied in next reboot self.builder.get_object("dialognetworkrestart").show() self.builder.get_object("dialogeditnetwork").hide() def on_acceptaddnetwork_clicked(self, widget, data=None): """ Function called when you accept "add network" window """ # Get if mac is auto-generated or manual if self.builder.get_object("radiomanual").get_active(): mac = self.builder.get_object("entrymac").get_text() else: mac = '' treeaddnetwork = self.builder.get_object("treeaddnetwork") listaddnetwork = self.builder.get_object("listaddnetwork") iter = treeaddnetwork.get_active_iter() network_ref = listaddnetwork.get_value(iter, 0) limit = self.builder.get_object("entrylimit").get_text() # Create new network self.xc_servers[self.selected_host].vm_add_interface(self.selected_ref, network_ref, mac, limit) # Hide then window self.builder.get_object("dialogaddnetwork").hide() def on_radiomanual_clicked(self, widget, data=None): """ Function called when on "add new network" you select a "manual" mac """ self.builder.get_object("entrymac").set_sensitive(True) mac = self.builder.get_object("entrymac").get_text() X = "([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}" c = re.compile(X).search(mac) # If characteres in MAC are valid.. if c: # You can accept the dialog self.builder.get_object("acceptaddnetwork").set_sensitive(True) else: # Else not self.builder.get_object("acceptaddnetwork").set_sensitive(False) def on_radioeditmanual_clicked(self, widget, data=None): """ Function called when on "edit network" you select a "manual" mac """ self.builder.get_object("entryeditmac").set_sensitive(True) mac = self.builder.get_object("entryeditmac").get_text() X = "([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}" c = re.compile(X).search(mac) # If characteres in MAC are valid.. if c: # You can accept the dialog self.builder.get_object("accepteditnetwork").set_sensitive(True) else: # Else not self.builder.get_object("accepteditnetwork").set_sensitive(False) def on_radioeditauto_clicked(self, widget, data=None): """ Function called when you selet radio "auto" (mac autogenerated) On edit network window """ # You can accept the dialog then self.builder.get_object("accepteditnetwork").set_sensitive(True) # But you cannot edit the mac textfield self.builder.get_object("entryeditmac").set_sensitive(False) def on_entryeditmac_changed(self, widget, data=None): """ Function called when you edit a "mac" text field """ mac = self.builder.get_object("entryeditmac").get_text() X = "([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}" c = re.compile(X).search(mac) # If characteres in MAC are valid.. if c: # You can accept the dialog self.builder.get_object("accepteditnetwork").set_sensitive(True) else: # Else not self.builder.get_object("accepteditnetwork").set_sensitive(False) def on_entrymac_changed(self, widget, data=None): """ Function called when you edit a "mac" text field """ mac = self.builder.get_object("entrymac").get_text() X = "([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}" c = re.compile(X).search(mac) # If characteres in MAC are valid.. if c: # You can accept the dialog self.builder.get_object("acceptaddnetwork").set_sensitive(True) else: # Else not self.builder.get_object("acceptaddnetwork").set_sensitive(False) def on_radioauto_clicked(self, widget, data=None): """ Function called when you selet radio "auto" (mac autogenerated) On add new network window """ self.builder.get_object("acceptaddnetwork").set_sensitive(True) self.builder.get_object("entrymac").set_sensitive(False) openxenmanager/oxcSERVER_alerts.py0000644000175000017500000000307411636446664015753 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERalerts: def dismiss_alert(self, ref): res = self.connection.message.destroy( self.session_uuid, ref) if "Value" in res: #del self.all_messages[ref] return 0 else: print res return 1 openxenmanager/oxcSERVER_host_network.py0000644000175000017500000001071711636446664017211 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERhostnetwork: def fill_listnetworknic(self, list): list.clear() vlan = [0] for pif_key in self.all_pif: pif = self.all_pif[pif_key] if pif['currently_attached']: if len(pif['bond_master_of']): devices = [] for slave in self.all_bond[pif['bond_master_of'][0]]['slaves']: devices.append(self.all_pif[slave]['device'][-1:]) devices.sort() list.append([pif_key,"Bond %s" % '+'.join(devices)]) else: if pif['VLAN'] == "-1": list.append([pif_key,"NIC %s" % pif['device'][-1:]]) else: vlan.append(pif['VLAN']) return int(max(vlan))+1 def delete_network(self, ref_network, ref_vm): print self.all_network[ref_network] for ref_pif in self.all_network[ref_network]['PIFs']: if len(self.all_pif[ref_pif]['bond_master_of']): self.delete_nic(ref_pif, ref_vm, False) else: res = self.connection.PIF.destroy(self.session_uuid, ref_pif) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res res = self.connection.network.destroy(self.session_uuid, ref_network) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res def create_external_network(self, name, desc, auto, pif, vlan): network_cfg = { 'uuid': '', 'name_label': name, 'name_description': desc, 'VIFs': [], 'PIFs': [], 'other_config': {}, 'bridge': '', 'blobs': {} } if auto: network_cfg['other_config']['automatic'] = "true" else: network_cfg['other_config']['automatic'] = "false" network = None res = self.connection.network.create(self.session_uuid, network_cfg) if "Value" in res: network = res['Value'] else: print res if network: res = self.connection.pool.create_VLAN_from_PIF(self.session_uuid, pif, network, str(vlan)) if "Value" not in res: print res def create_internal_network(self, name, desc, auto): network_cfg = { 'uuid': '', 'name_label': name, 'name_description': desc, 'VIFs': [], 'PIFs': [], 'other_config': {}, 'bridge': '', 'blobs': {} } if auto: network_cfg['other_config']['automatic'] = "true" else: network_cfg['other_config']['automatic'] = "false" res = self.connection.network.create(self.session_uuid, network_cfg) if "Value" in res: print res def is_vlan_available(self, data): for pif_key in self.all_pif: if int(self.all_pif[pif_key]['VLAN']) == int(data): return False return True openxenmanager/oxc.conf0000644000175000017500000000047411636446664013750 0ustar rrsrrs[gui] show_xs_templates = True show_custom_templates = True show_local_storage = True show_toolbar = True save_password = False master_password = "" [servers] [[hosts]] [maps] check_unused_network = False check_unused_storage = False check_show_storage = True check_show_network = True check_show_halted_vms = False openxenmanager/oxcSERVER_vm_storage.py0000644000175000017500000001753611636446664016637 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERvmstorage: def vm_storagedetach(self, ref): print self.connection.VBD.destroy(self.session_uuid, ref) def vm_storageplug(self, ref): print self.connection.VBD.plug(self.session_uuid, ref) def vm_storageunplug(self, ref): print self.connection.VBD.unplug(self.session_uuid, ref) def set_vm_dvd(self, vm, vdi): if vdi: cd = None if self.get_vm_dvd_ref(vm) and self.all_vbd[self.get_vm_dvd_ref(vm)]['allowed_operations'].count("eject"): res = self.connection.Async.VBD.eject(self.session_uuid, self.get_vm_dvd_ref(vm)) self.track_tasks[res['Value']] = vm if vdi != "empty": res = self.connection.Async.VBD.insert(self.session_uuid, self.get_vm_dvd_ref(vm), vdi) self.track_tasks[res['Value']] = vm def add_disk_to_vm(self, name, description, sr, virtual_size, vmuuid, vm_ref): vdi_cfg = { 'name_label': name, 'name_description': description, 'SR': sr, 'virtual_size': str(virtual_size), 'type': "user", 'sharable': False, 'read_only': False, 'other_config': {}, 'xenstore_data': {}, 'smconfig': {"vmhint" : vmuuid } } vdi = self.connection.VDI.create(self.session_uuid, vdi_cfg) if vm_ref: userdevice = self.connection.VM.get_allowed_VBD_devices(self.session_uuid, vm_ref)['Value'][0] vbd_cfg = { 'VM': vm_ref, 'VDI': vdi['Value'], 'userdevice': userdevice, 'bootable': False, 'mode': "RW", 'type': "Disk", 'unplugabble': "0", 'storage_lock': "0", 'empty': False, 'currently_attached': "0", 'status_code': "0", 'other_config': {}, 'qos_algorithm_type': "", 'qos_algorithm_params': {} } res = self.connection.VBD.create(self.session_uuid, vbd_cfg) if "Value" in res: self.track_tasks[res['Value']] = vm_ref else: print res res = self.connection.Async.VBD.plug(self.session_uuid, res['Value']) if "Value" in res: self.track_tasks[res['Value']] = vm_ref else: print res def fill_vm_storageattach(self, list): list.clear() refattachdisk = {} all_sr = self.connection.SR.get_all_records\ (self.session_uuid)['Value'] for sr in all_sr: if all_sr[sr]['type'] != "iso" and all_sr[sr]['content_type'] != "iso": refattachdisk[sr] = list.append(None, [gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"), sr, all_sr[sr]["name_label"],"", False]) all_vdi= self.connection.VDI.get_all_records\ (self.session_uuid)['Value'] for vdi in all_vdi: if not all_vdi[vdi]['VBDs'] and all_vdi[vdi]['read_only'] == False: list.append(refattachdisk[all_vdi[vdi]['SR']], [gtk.gdk.pixbuf_new_from_file("images/user_template_16.png"), vdi, all_vdi[vdi]['name_label'], all_vdi[vdi]['name_description'] + " - " + self.convert_bytes(all_vdi[vdi]['physical_utilisation']) + " used out of " + self.convert_bytes(all_vdi[vdi]['virtual_size']),True]) def attach_disk_to_vm(self, ref, vdi, ro): userdevice = self.connection.VM.get_allowed_VBD_devices(self.session_uuid, ref)['Value'][0] vbd_cfg = { 'VM': ref, 'VDI': vdi, 'userdevice': userdevice, 'bootable': False, 'mode': "RW", 'type': "Disk", 'unplugabble': "0", 'storage_lock': "0", 'empty': False, 'currently_attached': "0", 'status_code': "0", 'other_config': {}, 'qos_algorithm_type': "", 'qos_algorithm_params': {} } if ro == True: vbd_cfg["mode"] = "RO" res = self.connection.VBD.create(self.session_uuid, vbd_cfg) if "Value" in res: res = self.connection.Async.VBD.plug(self.session_uuid, res["Value"]) if "Value" in res: self.track_tasks[res['Value']] = ref def install_xenserver_tools(self, vm): vdi = self.get_xs_tools_ref() if self.get_vm_dvd_ref(vm) and self.all_vbd[self.get_vm_dvd_ref(vm)]['allowed_operations'].count("eject"): res = self.connection.Async.VBD.eject(self.session_uuid, self.get_vm_dvd_ref(vm)) self.track_tasks[res['Value']] = vm if vdi != "empty": res = self.connection.Async.VBD.insert(self.session_uuid, self.get_vm_dvd_ref(vm), vdi) self.track_tasks[res['Value']] = vm def get_xs_tools_ref(self): for vdi in self.all_vdi: if "sm_config" in self.all_vdi[vdi] and "xs-tools" in self.all_vdi[vdi]["sm_config"] \ and self.all_vdi[vdi]["sm_config"]["xs-tools"] == "true": return vdi def get_vm_dvd_ref(self, vm): for vbd in self.all_vbd.keys(): if self.all_vbd[vbd]["VM"] == vm: if (self.all_vbd[vbd]['type'] == "CD" or self.all_vbd[vbd]['type'] == "udev"): print self.all_vbd[vbd]['type'] return vbd #FIXME: auto add VBD to CD, do 'click here to add CD' userdevice = self.connection.VM.get_allowed_VBD_devices(self.session_uuid, vm)['Value'][0] vbd_cfg = { 'uuid': "", 'allowed_operations': [], 'current_operations': {}, 'VM': vm, 'VDI': "", 'device': "", 'userdevice': userdevice, 'bootable': False, 'mode': "RO", 'type': "CD", 'unplugabble': "0", 'storage_lock': "0", 'empty': True, 'other_config': {}, 'currently_attached': "0", 'status_code': "0", 'status_detail': "", 'runtime_properties': "", 'qos_algorithm_type': "", 'qos_algorithm_params': {}, 'metrics': "" } res = self.connection.VBD.create(self.session_uuid, vbd_cfg) if "Value" in res: self.track_tasks[res['Value']] = vm self.all_vbd[res['Value']] = self.connection.VBD.get_record(self.session_uuid, res['Value'])['Value'] else: print res return res['Value'] openxenmanager/oxcSERVER_host_nics.py0000644000175000017500000001241511636446664016451 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERhostnics: def delete_nic(self, ref_nic, ref_vm, delete_network=True): ref_bond = self.all_pif[ref_nic]['bond_master_of'][0] ref_network = self.all_pif[ref_nic]['network'] res = self.connection.Bond.destroy(self.session_uuid, ref_bond) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res if delete_network: res = self.connection.network.destroy(self.session_uuid, ref_network) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res def fill_available_nics(self, list, list2): list.clear() list2.clear() for pif_key in self.all_pif.keys(): if self.all_pif[pif_key]['metrics'] != "OpaqueRef:NULL": pif_metric = {} if self.all_pif[pif_key]['metrics'] in self.all_pif_metrics: pif_metric = self.all_pif_metrics[self.all_pif[pif_key]['metrics']] else: pif_metric["pci_bus_path"] = "N/A" pif = self.all_pif[pif_key] if self.all_pif[pif_key]['metrics'] != "OpaqueRef:NULL" and pif_metric['pci_bus_path'] != "N/A": nic = "NIC %s" % pif['device'][-1:] error = "" if len(self.all_network[pif['network']]['VIFs']): error = "in use by VMs" if pif['bond_slave_of'] != "OpaqueRef:NULL" and pif['bond_slave_of'] in self.all_bond: devices = [] for slave in self.all_bond[pif['bond_slave_of']]['slaves']: devices.append(self.all_pif[slave]['device'][-1:]) devices.sort() error = "already in Bond %s" % ('+'.join(devices)) list.append([pif_key,nic,error,error == ""]) def create_bond(self, ref, ref2, name, name2,auto=False): network_cfg = { 'uuid' : '', 'name_label': "Bond %s+%s" % (name[-1:], name2[-1:]), 'name_description': '', 'VIFs': [], 'PIFs': [], 'other_config': { 'XenCenterCreateInProgress': "true" }, 'bridge': '', 'blobs': {} } if auto: network_cfg['other_config']['automatic'] = "true" else: network_cfg['other_config']['automatic'] = "false" res = self.connection.network.create(self.session_uuid, network_cfg) if "Value" in res: self.track_tasks[res['Value']] = ref network = res['Value'] else: print res res = self.connection.Async.Bond.create(self.session_uuid, network, [ref, ref2],"") if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def fill_nic_info(self, ref): pif = self.all_pif[ref] pif_metric = self.all_pif_metrics[self.all_pif[ref]['metrics']] if pif_metric['duplex']: duplex = "full" else: duplex = "half" if "mac" in pif: mac = pif['mac'] else: mac = "" connected = "Disconnected" if pif_metric['carrier']: connected = "Connected" labels = {} labels['lblnicname'] = "NIC %s" % pif['device'][-1:] labels['lblnicvendor'] = pif_metric['vendor_name'] labels['lblnicdevice'] = pif_metric['device_name'] labels['lblnicmac'] = mac labels['lblnicpcibus'] = pif_metric['pci_bus_path'] labels['lblniclinkstatus'] = connected if connected == "Connected": labels['lblnicspeed'] = pif_metric['speed'] + " mbit/s" else: labels['lblnicspeed'] = "" labels['lblnicduplex'] = duplex for label in labels.keys(): self.wine.builder.get_object(label).set_label(labels[label]) openxenmanager/window_vm.py0000644000175000017500000004563111636446664014677 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python from window_vm_network import * from window_vm_storage import * from window_vm_snapshot import * from window_vm_performance import * import gtk import time selection = None class oxcWindowVM(oxcWindowVMNetwork,oxcWindowVMStorage,oxcWindowVMSnapshot,oxcWindowVMPerformance): """ Class to manage window actions """ def update_memory_tab(self): if self.treeview.get_cursor()[1]: dynamicmin = self.xc_servers[self.selected_host].all_vms[self.selected_ref]["memory_dynamic_min"] dynamicmax = self.xc_servers[self.selected_host].all_vms[self.selected_ref]["memory_dynamic_max"] staticmin = self.xc_servers[self.selected_host].all_vms[self.selected_ref]["memory_static_min"] staticmax = self.xc_servers[self.selected_host].all_vms[self.selected_ref]["memory_static_max"] ishvm = self.xc_servers[self.selected_host].all_vms[self.selected_ref]["HVM_boot_policy"] if ishvm: self.builder.get_object("lbldynamicmin").set_label(self.convert_bytes_mb(dynamicmin) + " MB") self.builder.get_object("lbldynamicmax").set_label(self.convert_bytes_mb(dynamicmax) + " MB") self.builder.get_object("lblstaticmax").set_label(self.convert_bytes_mb(staticmax) + " MB") self.builder.get_object("txtdynamicmin").set_text(self.convert_bytes_mb(dynamicmin)) self.builder.get_object("txtdynamicmax").set_text(self.convert_bytes_mb(dynamicmax)) self.builder.get_object("txtstaticmax").set_text(self.convert_bytes_mb(staticmax)) self.builder.get_object("tabboxmem").set_current_page(0) else: self.builder.get_object("lbldynamicmin1").set_label(self.convert_bytes_mb(dynamicmin) + " MB") self.builder.get_object("lbldynamicmax1").set_label(self.convert_bytes_mb(dynamicmax) + " MB") self.builder.get_object("txtfixedmemory").set_text(self.convert_bytes_mb(staticmax)) self.builder.get_object("txtdynamicmin1").set_text(self.convert_bytes_mb(dynamicmin)) self.builder.get_object("txtdynamicmax1").set_text(self.convert_bytes_mb(dynamicmax)) self.builder.get_object("radiomemstatic").set_active(dynamicmin == dynamicmax) self.builder.get_object("radiomemdynamic").set_active(dynamicmin != dynamicmax) self.builder.get_object("tabboxmem").set_current_page(1) def on_btapplymemory_clicked(self, widget, data=None): dynamicmin = self.builder.get_object("txtdynamicmin").get_text() dynamicmax = self.builder.get_object("txtdynamicmax").get_text() staticmax = self.builder.get_object("txtstaticmax").get_text() self.xc_servers[self.selected_host].set_memory(self.selected_ref, dynamicmin, dynamicmax, staticmax) def on_btapplymemory1_clicked(self, widget, data=None): if self.builder.get_object("radiomemstatic").get_active(): minimun = maximun = self.builder.get_object("txtfixedmemory").get_text() self.xc_servers[self.selected_host].set_memory_limits(self.selected_ref, minimun, maximun, minimun, maximun) else: minimun = self.builder.get_object("txtdynamicmin1").get_text() maximun = self.builder.get_object("txtdynamicmax1").get_text() self.xc_servers[self.selected_host].set_memory_limits(self.selected_ref, minimun, maximun, minimun, maximun) def on_btsendctraltdel_clicked(self, widget, data=None): """ Function called when you press "send ctrl alt del" on vm console """ self.on_menuitem_tools_cad_activate(widget, data) def vnc_button_release(self, clipboard, data, user=None): global selection selection = data self.vnc.client_cut_text(data) return def copy_cb(self, clipboard, data, info, user=None): global selection data.set_text(selection, -1) def clear_cb(self, clipboard, data, user=None): return def on_btcopytext_clicked(self, widget, data=None): """ Function called when you press "Copy selected text" on console tab """ clipboard = self.vnc.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD) clipboard.connect("owner-change", self.vnc_button_release) text = clipboard.wait_for_text() targets = [('TEXT', 0, 1), ('STRING', 0, 2), ('COMPOUND_TEXT', 0, 3), ('UTF8_STRING', 0, 4)] clipboard.set_with_data(targets, self.copy_cb, self.clear_cb, None); def text_get_func(clipboard, text, data): if text: self.vnc.client_cut_text(text) clipboard.request_text(text_get_func) def on_btundockconsole_clicked(self, widget, data=None): """ Function called when you press "undock" """ self.noclosevnc = True self.builder.get_object("windowvncundock").show() self.builder.get_object("console_area").remove(self.vnc) self.builder.get_object("console_area3").add(self.vnc) def on_btredockconsole_clicked(self, widget, data=None): """ Function called when you press "redock" """ self.builder.get_object("windowvncundock").hide() self.builder.get_object("console_area3").remove(self.vnc) self.builder.get_object("console_area").add(self.vnc) def on_btenterfullscreen_clicked(self, widget, data=None): """ Function called when you press "enter fullscreen" """ self.builder.get_object("windowvnc").show() self.builder.get_object("console_area").remove(self.vnc) self.builder.get_object("console_area2").add(self.vnc) self.builder.get_object("windowvnc").fullscreen() def on_btexitfullscreen_clicked(self, widget, data=None): """ Function called when you press "exit fullscreen" """ self.builder.get_object("windowvnc").hide() self.builder.get_object("console_area2").remove(self.vnc) self.builder.get_object("console_area").add(self.vnc) def on_windowcopyvm_cancel_activate(self, widget, data=None): """ Function called when you cancel "window copy" window """ self.builder.get_object("windowcopyvm").hide() def on_windowcopyvm_copy_activate(self, widget, data=None): """ Function called when you accept "window copy" window """ listcopystg = self.builder.get_object("listcopystg") treecopystg = self.builder.get_object("treecopystg") # Get the name and description name = self.builder.get_object("txtcopyvmname").get_text() desc = self.builder.get_object("txtcopyvmdesc").get_text() full = False # Check if "fast clone" is selected or "full clone" if self.builder.get_object("radiofullclone").get_active(): full = True # Get the selected storage selection = treecopystg.get_selection() if selection.get_selected()[1] == None: iter = listcopystg.get_iter((0,1)) else: iter = selection.get_selected()[1] sr = listcopystg.get_value(iter, 1) # Call to function to copy the vm self.xc_servers[self.selected_host].copy_vm(self.selected_ref, name, desc, sr, full) self.builder.get_object("windowcopyvm").hide() def on_btimportaddnetwork_clicked(self, widget, data=None): """ Function called whe you press add a new network when you are doing "import" process """ treeimportservers = self.builder.get_object("treeimportservers") listimportservers = self.builder.get_object("listimportservers") selection = treeimportservers.get_selection() host = listimportservers.get_value(selection.get_selected()[1], 3) listimportnetworks = self.builder.get_object("listimportnetworks") # Get first network as default and network ref network = self.xc_servers[host].first_network() network_ref = self.xc_servers[host].first_network_ref() # Add to network list listimportnetworks.append(["interface " + str(listimportnetworks.__len__()), "auto-generated", network, network_ref ]) def on_btimportdeletenetwork_clicked(self, widget, data=None): """ Function called whe you press delete a network when you are doing "import" process """ listimportnetworks = self.builder.get_object("listimportnetworks") treeimportnetworks = self.builder.get_object("treeimportnetworks") selection = treeimportnetworks.get_selection() # Get selected iter = selection.get_selected()[1] # And remove from list listimportnetworks.remove(iter) def on_dialogdelete_cancel_activate(self, widget, data=None): """ Function called when you cancel the "delete vm" confirmation """ self.builder.get_object("dialogdeletevm").hide() def on_dialogdelete_accept_activate(self, widget, data=None): """ Function called when you cancel the "delete vm" confirmation """ # Get if "delete disks" and "delete snapshots" are active delete_vdi = self.builder.get_object("dialogdelete_vdi").get_active() delete_snap = self.builder.get_object("dialogdelete_snap").get_active() # Remove first from list self.treestore.remove(self.selected_iter) # And late remove from server self.xc_servers[self.selected_host].destroy_vm(self.selected_ref, delete_vdi, delete_snap) # And hide confirmation window self.builder.get_object("dialogdeletevm").hide() def on_filechooserimportvm_file_set(self, widget, data=None): """" Function called when you select a file to import """ # Enable "Next >" button because filename was selected self.builder.get_object("nextvmimport").set_sensitive(True) def on_tabboximport_switch_page(self, widget, data=None, data2=None): """ Function called when you change the page in "import vm" process """ # Set colors.. white = gtk.gdk.color_parse("white") blue = gtk.gdk.color_parse("#d5e5f7") for i in range(0,5): self.builder.get_object("eventimport" + str(i)).modify_bg(gtk.STATE_NORMAL, white) self.builder.get_object("eventimport" + str(data2)).modify_bg(gtk.STATE_NORMAL, blue) # If page is the first, you cannot go to previous page self.builder.get_object("previousvmimport").set_sensitive(data2 != 0) def on_nextvmimport_clicked(self, widget, data=None): """ Function called when you press "Next" button on Import VM process """ # Get the current page page = self.builder.get_object("tabboximport").get_current_page() # Move the tabbox to next tab self.builder.get_object("tabboximport").set_current_page(page+1) if page+1 == 1: # If next page is the second.. treeimportservers = self.builder.get_object("treeimportservers") selection = treeimportservers.get_selection().get_selected()[1] # If in possible servers to import there is one element, enable "Next" button self.builder.get_object("nextvmimport").set_sensitive(selection != None) if page+1 == 2: # If next page is the third.. treeimportservers = self.builder.get_object("treeimportservers") listimportservers = self.builder.get_object("listimportservers") selection = treeimportservers.get_selection() # Get selected host host = listimportservers.get_value(selection.get_selected()[1], 3) listimportstg = self.builder.get_object("listimportstg") # Fill the list of possible storage to import the VM, returns default storage position defstg = self.xc_servers[host].fill_importstg(listimportstg) treeimportstg = self.builder.get_object("treeimportstg") # Select the "default storage" treeimportstg.set_cursor((defstg, ), treeimportstg.get_column(0)) treeimportstg.get_selection().select_path((defstg, )) listimportnetworks = self.builder.get_object("listimportnetworks") listimportnetworkcolumn = self.builder.get_object("listimportnetworkcolumn") # Fill the list the networks with option "automatically add to new servers" self.xc_servers[host].fill_list_networks(listimportnetworks, listimportnetworkcolumn) # If page is the third, button is called "Import >" widget.set_label("Import >") else: # If next page is different to third, the button is called "Next >" widget.set_label("Next >") if page+1 == 3: # If page is the fourth.. filename = self.builder.get_object("filechooserimportvm").get_filename() treeimportservers = self.builder.get_object("treeimportservers") listimportservers = self.builder.get_object("listimportservers") selection = treeimportservers.get_selection() # Get selected host host = listimportservers.get_value(selection.get_selected()[1], 3) treeimportstg = self.builder.get_object("treeimportstg") listimportstg = self.builder.get_object("listimportstg") selection = treeimportstg.get_selection() # Get selected storage sr = listimportstg.get_value(selection.get_selected()[1], 1) self.xc_servers[host].halt_import = False # Show a progress with import progerss self.builder.get_object("wprogressimportvm").show() # And begin to import the VM self.xc_servers[host].thread_import_vm(sr, filename) if page+1 == 4: # If page is the last.. widget.set_sensitive(False) # Then enable "finish" button self.builder.get_object("finishvmimport").set_sensitive(True) def on_previousvmimport_clicked(self, widget, data=None): """" Function called when you press "< Previous" button """ page = self.builder.get_object("tabboximport").get_current_page() # Move to previous tab self.builder.get_object("tabboximport").set_current_page(page-1) # And set next button with correct label self.builder.get_object("nextvmimport").set_label("Next >") def on_cancelvmimport_clicked(self, widget, data=None): """ Function called when you cancel the import vm process """ treeimportservers = self.builder.get_object("treeimportservers") listimportservers = self.builder.get_object("listimportservers") selection = treeimportservers.get_selection() if selection.get_selected()[1]: # Get the selected host host = listimportservers.get_value(selection.get_selected()[1], 3) # Stop the import self.xc_servers[host].halt_import = False # hide the window self.builder.get_object("vmimport").hide() def on_finishvmimport_clicked(self, widget, data=None): """ Function called when you press the "Finish" button """ treeimportservers = self.builder.get_object("treeimportservers") listimportservers = self.builder.get_object("listimportservers") selection = treeimportservers.get_selection() host = listimportservers.get_value(selection.get_selected()[1], 3) vif_cfg = { 'name': 'API_VIF', 'type': 'ioemu', 'device': '0', 'network': '', 'MAC': '', 'MTU': '0', "qos_algorithm_type": "ratelimit", "qos_algorithm_params": {}, "other_config": {} } selection = self.builder.get_object("treeimportnetworks").get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) selection.select_all() model, selected = selection.get_selected_rows() iters = [model.get_iter(path) for path in selected] # For each network... for iter in iters: network = self.builder.get_object("listimportnetworks").get_value(iter, 3) vm = self.xc_servers[host].import_ref # Add to new imported VM self.xc_servers[host].vm_add_interface(vm, network, None, "0") # Sleep 1 second between action time.sleep(1) # Set if "file is a imported template" or/and "start after import" self.xc_servers[host].import_start = self.builder.get_object("checkstartvmafterimport").get_active() self.xc_servers[host].import_make_into_template = self.builder.get_object("radioexportedtpl").get_active() # Hide the window selection.set_mode(gtk.SELECTION_SINGLE) self.builder.get_object("vmimport").hide() def on_networkcolumn1_changed(self, widget, data=None, data2=None): """ Function called when you change the network combo for selected interface """ treeimportnetworks = self.builder.get_object("treeimportnetworks") listimportnetworks = self.builder.get_object("listimportnetworks") listnetworkcolumn = self.builder.get_object("listimportnetworkcolumn") selection = treeimportnetworks.get_selection() iter = selection.get_selected()[1] listimportnetworks.set_value(iter, 2, listnetworkcolumn.get_value(data2, 0)) listimportnetworks.set_value(iter, 3, listnetworkcolumn.get_value(data2, 1)) def on_radiofastclone_toggled(self, widget, data=None): """ Function called when you toggle "fast clone" or "full clone" """ if widget.get_active(): if gtk.Buildable.get_name(widget) == "radiofastclone": self.builder.get_object("treecopystg").set_sensitive(False) else: self.builder.get_object("treecopystg").set_sensitive(True) openxenmanager/window_tools.py0000644000175000017500000001702611636446664015412 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python from xva import Xva from threading import Thread import gtk class ProgressBarOXC: widget = None widget2 = None def __init__(self, widget, widget2): self.widget = widget self.widget2 = widget2 self.widget.show() def update_amount(self, new_amount = None): value = "%.2f" % new_amount if float(value) > 1: value=1 self.widget.set_fraction(float(value)) def update_text(self, text= None): self.widget.set_text(text) def finish(self): image = gtk.Image() image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) self.widget2.set_image(image) self.widget2.set_label("Close") class oxcWindowTools: """ Class to manage OXC Tools """ def on_cancelmigratetool_clicked(self, widget, data=None): """ Cancel button pressed on migrate tool """ self.builder.get_object("migratetool").hide() def on_acceptmigratetool_clicked(self, widget, data=None): """ Accept button pressed on migrate tool """ machine = Xva(classProgressBar=ProgressBarOXC(self.builder.get_object("progressmigrate"), self.builder.get_object("cancelmigratetool"))) if not self.builder.get_object("fileossxenconfig").get_filename() and \ not self.builder.get_object("fileadddisk").get_filename(): return if self.builder.get_object("fileossxenconfig").get_filename(): print self.builder.get_object("fileossxenconfig").get_filename() params = {} execfile(options.config,params) if "name" in params: machine.set_name( params['name'] ) if "vpus" in params: machine.set_vcpus( params['vcpus'] ) if "kernel" in params: if params['kernel'].endswith("hvmloader"): machine.is_hvm() else: print "Kernels that are loaded from the Dom0 aren't supported. Use pygrub" sys.exit(255) else: machine.is_pv() if "disk" in params and len(params['disk']) != 0: for disk in params['disk']: (path, device, mode) = disk.split(",") path_split = path.split(":") path_split.reverse() machine.add_disk(path_split[0]) else: print "You need at least 1 Disk, Exiting" sys.exit(254) if "memory" in params: try: memory = int(params['memory'] ) machine.set_memory( memory * 1024 * 1024) except: print "Could parse memory, setting to 256M" machine.set_memory(268435456) if "apic" in params and params['apic'] == 0: machine.set_apic(False) if "acpi" in params and params['acpi'] == 0: machine.set_acpi(False) if "nx" in params and params['nx'] == 1: machine.set_nx(options.nx) if "pae" in params and params['pae'] == 0: machine.set_pae(False) else: # Set VM name machine.set_name(self.builder.get_object("txtmigratename").get_text()) # Set VM vcpus machine.set_vcpus(self.builder.get_object("spinmigratevcpus").get_text()) # Set VM ACPI machine.set_acpi(self.builder.get_object("checkmigrateacpi").get_active()) # Set VM ACIP machine.set_apic(self.builder.get_object("checkmigrateapic").get_active()) # Set VM Viridian machine.set_viridian(self.builder.get_object("checkmigrateviridian").get_active()) # Set VM PAE machine.set_pae(self.builder.get_object("checkmigratepae").get_active()) # Set VM NX machine.set_nx(self.builder.get_object("checkmigratenx").get_active()) # Set VM Memory memory = int(self.builder.get_object("spinmigratemem").get_text())*1024*1024 machine.set_memory(memory) # Add disk machine.add_disk(self.builder.get_object("fileadddisk").get_filename()) if self.builder.get_object("radiomigratehvm").get_active(): machine.is_hvm() else: machine.is_pv() sparse = self.builder.get_object("checkmigratesparse").get_active() # Save import sys #sys.stdout = labelStream(self.builder.get_object("lblmigrateprogress")) if self.builder.get_object("checkmigrateoutputxva").get_active(): # If save to xva file.. filename = self.builder.get_object("txtoutputxva").get_text() Thread(target=machine.save_as, kwargs={"filename":filename, "sparse":sparse}).start() else: # Else export to server.. server = self.xc_servers[self.selected_host].host username = self.xc_servers[self.selected_host].user password = self.xc_servers[self.selected_host].password ssl = self.xc_servers[self.selected_host].ssl Thread(target=machine.save_as, kwargs={"server":server, "username":username, "password":password, "ssl":ssl, "sparse":sparse}).start() widget.set_sensitive(False) #self.builder.get_object("migratetool").hide() def on_helpmigratetool_clicked(self, widget, data=None): """ Help button pressed on migrate tool """ self.builder.get_object("migratetoolhelp").show() def on_closemigratetoolhelp_clicked(self, widget, data=None): """ Closebutton pressed on migrate tool help """ self.builder.get_object("migratetoolhelp").hide() def on_btoutputxva_clicked(self, widget, data=None): """ Function called when you press "choose xva file" """ # Show file chooser self.builder.get_object("fileoutputxva").show() def on_acceptfileoutputxva_clicked(self, widget, data=None): """ Function called when you accept output xva file chooser """ filename = self.builder.get_object("fileoutputxva").get_filename() self.builder.get_object("txtoutputxva").set_text(filename) self.builder.get_object("fileoutputxva").hide() def on_cancelfileoutputxva_clicked(self, widget, data=None): """ Function called when you accept output xva file chooser """ self.builder.get_object("fileoutputxva").hide() openxenmanager/window_vm_snapshot.py0000644000175000017500000001655711636446664016623 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python from threading import Thread class oxcWindowVMSnapshot: """ Class to manage "snapshots" of a VM """ def on_btcancelsnapshotname_clicked(self, widget, data=None): """ Function called when you cancel the "set snapshot name" dialog """ # Hide dialog self.builder.get_object("dialogsnapshotname").hide() def on_btacceptsnapshotname_clicked(self, widget, data=None): """ Function called when you cancel the "set snapshot name" dialog """ # Call to take_snapshot function with typed name self.xc_servers[self.selected_host].take_snapshot(self.selected_ref, self.builder.get_object("snapshotname").get_text()) # Hide dialog self.builder.get_object("dialogsnapshotname").hide() def on_bttakesnapshot_clicked(self, widget, data=None): """ Function called when you press "Take snapshot" """ # Empty the name of snapshot self.builder.get_object("snapshotname").set_text("") # Show the dialog asking snapshot name self.builder.get_object("dialogsnapshotname").show() def on_m_snap_newvm_activate(self, widget, data=None): # print self.selected_snap_ref # TODO -> select vm with name_label """ Function called when you press "Take snapshot" """ self.on_m_newvm_activate(widget, data) def on_m_snap_createtpl_activate(self, widget, data=None): """ Function called when you press "create template from snapshot" """ self.builder.get_object("snaptplname").set_text("Template from snapshot '" + \ self.xc_servers[self.selected_host].all_vms[self.selected_snap_ref]['name_label'] + "'") # Show the dialog asking the new template name self.builder.get_object("dialogsnaptplname").show() def on_m_snap_delete_activate(self, widget, data=None): """ Function called when you press "delete snapshot" """ # Show the confirmation dialog self.builder.get_object("dialogsnapshotdelete").show() def on_m_snap_export_activate(self, widget, data=None): """ Function called when you press "export snapshot" """ # Set default name self.export_snap = True self.filesave.set_current_name(self.xc_servers[self.selected_host].all_vms[self.selected_snap_ref]['name_label'] + ".xva") # Show the choose dialog self.filesave.show() def on_m_snap_export_vm_activate(self, widget, data=None): """ Function called when you press "export snapshot" """ # Set default name self.export_snap_vm = True self.filesave.set_current_name(self.xc_servers[self.selected_host].all_vms[self.selected_snap_ref]['name_label'] + ".xva") # Show the choose dialog self.filesave.show() def on_btacceptsnapshotdelete_clicked(self, widget, data=None): """ Function called when you accept the "delete snapshot" confirmation dialog """ # Delete the snapshot Thread(target=self.xc_servers[self.selected_host].delete_snapshot, args=(self.selected_snap_ref, self.selected_ref)).start() # And hide the confirmation dialog self.builder.get_object("dialogsnapshotdelete").hide() def on_btcancelsnapshotdelete_clicked(self, widget, data=None): """ Function called when you cancel the "delete snapshot" confirmation dialog """ # Hide the confirmation dialog self.builder.get_object("dialogsnapshotdelete").hide() def on_btacceptsnaptplname_clicked(self, widget, data=None): """ Function called when you accept the "specify name" dialog to create a template from snapshot """ # Call to function to create a new template from snapshot self.xc_servers[self.selected_host].create_template_from_snap(self.selected_snap_ref, \ self.builder.get_object("snaptplname").get_text()) # Hide the dialog self.builder.get_object("dialogsnaptplname").hide() def on_btcancelsnaptplname_clicked(self, widget, data=None): """ Function called when you cancel the "specify name" dialog to create a template from snapshot """ # Hide the dialog self.builder.get_object("dialogsnaptplname").hide() def on_treevmsnapshots_button_press_event(self, widget, event): """ Function called when you press with the mouse inside "snapshots" tree """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo widget.grab_focus() widget.set_cursor( path, col, 0) iter = self.builder.get_object("listvmsnapshots").get_iter(path) self.builder.get_object("btsnapnewvm").set_sensitive(iter != None) self.builder.get_object("btsnapcreatetpl").set_sensitive(iter != None) self.builder.get_object("btsnapexport").set_sensitive(iter != None) self.builder.get_object("btsnapdelete").set_sensitive(iter != None) # Set in a global variable the selected snapshot self.selected_snap_ref = self.builder.get_object("listvmsnapshots").get_value(iter, 0) ops = self.xc_servers[self.selected_host].all_vms[self.selected_snap_ref]['allowed_operations'] self.builder.get_object("btsnaprevert").set_sensitive("revert" in ops) if event.button == 3: # If button pressed is the right.. # Show a menu with snapshot options menu_snapshot = self.builder.get_object("menu_snapshot") menu_snapshot.popup( None, None, None, event.button, time) def on_btsnaprevert_clicked(self, widget, data=None): """ Function called when you press "Revert to..." on a snapshot """ self.builder.get_object("dialogrevert").show() def on_canceldialogrevert_clicked(self, widget, data=None): """ Function called when you cancel revert dialog """ self.builder.get_object("dialogrevert").hide() def on_acceptdialogrevert_clicked(self, widget, data=None): """ Function called when you cancel revert dialog """ self.xc_servers[self.selected_host].revert_to_snapshot(self.selected_ref, self.selected_snap_ref) self.builder.get_object("dialogrevert").hide() openxenmanager/capabilities.py0000644000175000017500000001144211636446664015310 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # Copyright (C) 2011 Cheng Sun # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- capabilities_conf_text = [ "These files will contain personally identifiable information", "These files may contain personally identifiable information", "These files may contain personally identifiable information if customized", "These files will contain no personally identifiable information", ] capabilities_text = { "host-crashdump-dumps" : ["Crash dump files", "Memory dump created on server crash, if you select this option you will be prompted for these files to be removed after the report has been compiled"], "firstboot" : ["First-boot scripts", "The scripts run for initializing local storage repositories and networking"], "network-status" : ["Network status", "Current status of the network interfaces, routing tables and firewall"], "process-list" : ["Process listing", "A complete listing of the processes running in a tree format"], "xenserver-databases" : ["XenServer database", "The database which stores the state of the XenServer"], "disk-info" : ["Disk information", "Partition tables, free space, iSCSI and LVM configuration"], "hardware-info" : ["Hardware information", "Details of the processors, memory and other basic hardware"], "high-availability" : ["Hight availability", "Log file from the high availability daemon"], "xha-liveset" : ["High availability liveset", "High availability liveset"], "xen-info" : ["Hypervisor configuration", "Details of the hypervisor version and its current state"], "kernel-info" : ["Kernel information", "Details of kernel modules, filesystems devices and kernel configuration"], "loopback-devices" : ["Loopback devices", "Details of loopback devices"], "multipath" : ["Multipathing configuration", "Retrieves the server's storage multipathing configuration"], "persistent-stats" : ["Persistent statistics", "Persistent performance statistics"], "system-logs" : ["System logs", "Logfiles which record the activity of various system processes"], "vncterm" : ["VNCTerm crash dumps", "Crash dumps files from the VNCTerm daemon"], "xenserver-config" : ["XenServer Configuration", "Details of the XenServer such as version and build information, primary hard disk location and pool configuration"], "xapi-debug" : ["XenServer daemon crash dumps", "XenServer daemon crash dumps"], "xenserver-install" : ["XenServer installation log files", "Log files generated during the installation of the XenServer"], "xenserver-logs" : ["Xenserver logs", "Log files concerning the XenServer's activity"], "network-config" : ["Network scripts", "Configuration files used to bring up network interfaces, provide name resolution and enable the firewall"], "yum" : ["RPM package database", "YUM repository information and RPM package database listing"], "pam" : ["Authentication module configuration", ""], "boot-loader" : ["Boot loader configuration", "List of boot options and their details given to the user at system boot time"], "CVSM" : ["Citrix StorageLink configuration", "The configuration settings of Citrix StorageLink"], "host-crashdump-logs" : ["Crash dump logs", "Log files generated at time of server crash"], "hdparm-t" : ["Local performance test", "Performs a number of timing tests on each local hard disk, these may take up to one minute to complete"], "tapdisk-logs" : ["Storage subsystem logs", "Storage subsystem logs"], "system-services" : ["System services", "A listing of configured system services"], "blobs" : ["User-created binary objects", ""], "wlb" : ["Workload Balancing status", "Logs and status information from the WLB server monitoring this pool."], "X11" : ["X server logs", "Log files from the X server"], "X11-auth" : ["X11 authority", "X server authority files"], "xapi-subprocess" : ["Xenserver daemon subprocesses", "XenServer daemon process details"], "xenserver-domains" : ["XenServer domains list", "List of the guest VMs installed onto the server and their current states"] } openxenmanager/oxc.glade0000644000175000017500000671655311636446664014121 0ustar rrsrrs False 5 center normal OpenXenManager $Rev$: Revision of last commit $Author$: Author of last commit $Date$: Date of last commit Copyright © 2009-2010 Alberto González Copyright © 2011 Cheng Sun Free cross-platform interface for managing XenServer / XCP https://sourceforge.net/projects/openxenmanager/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MER- CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA True True False 2 True False end False True end 0 True False OpenXenManager SVN Version True True 2 True False True True 3 False Create Bond center True False 660 430 True False 100 20 True False Available NICs: 20 28 Add -> 100 20 True False True True False 280 95 <- Remove 100 20 True False True True False 280 120 100 20 True False Bonded NICs 395 28 570 165 False 0 True False 12 True False 50 20 True False 0 Vendor: 20 5 100 20 True False 0 Device: 20 25 100 20 True False 0 MAC Address: 20 45 55 20 True False 0 PCI bus: 20 65 80 20 True False 0 Link Status: 20 85 50 20 True False 0 Speed: 20 105 100 20 True False 0 Duplex: 20 125 20 True False 0 label 180 5 20 True False 0 label 180 25 20 True False 0 label 180 45 20 True False 0 label 180 65 20 True False 0 label 180 85 20 True False 0 label 180 105 20 True False 0 label 180 125 True False <b>NIC 0</b> True 40 208 Automatically add this network to new virtual machine. 400 20 True True False False True 30 390 Create 80 20 True False True True False 445 390 Cancel 80 20 True True True False 530 390 250 140 True True automatic automatic in True False queue none True True listavailnics False False column 3 1 3 2 20 50 250 140 True True automatic automatic in True False queue none True True listbondnics False False column 1 390 50 False 5 Add new Server True center dialog True False 2 True False end Connect True False True True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False 10 400 20 True False 10 Connect to a server by entering its IP address or hostname: False False 0 True False 80 80 True False images/server.jpg False False 0 True False 4 2 10 5 True False 0 Hostname: GTK_FILL True False 0 User name: 2 3 GTK_FILL True False 0 Password: 3 4 GTK_FILL 200 20 True True False â— True False False True True 1 2 3 4 GTK_EXPAND 200 20 True True â— True root True False False True True 1 2 2 3 GTK_EXPAND 200 20 True False listaddserverhosts center 0 True True True False False True True 1 2 GTK_EXPAND True False 0 Port: 1 2 GTK_FILL True False 10 True True â— 80 False False True True True True 0 SSL Connection True True False False True False True 1 1 2 1 2 GTK_EXPAND True True 1 True True 1 True True 1 connectAddServer cancelAddServer False 5 Add New Server center 320 260 normal True False 2 True False end False True end 0 True False True False True False True False images/server.jpg True True 0 False True 0 True False True False Connect to a server by entering its IP address or hostname: True True 0 True False 200 30 True False 169 22 100 20 True False 62 22 True True 1 True False 200 30 True False 169 22 100 20 True False 62 22 True True 2 True False 200 20 True False 169 22 100 20 True False 62 22 True True 3 True True 1 True True 1 1 100000 1 10 100 1 10 1 10 1 1 10 1 4094 1 1 10 64512 256 64 256 1 100 1 1 10 9 1 1 65536 1 10 5000 1 10 480 270 False 5 Change Server Password center normal True False 2 True False end OK True False True True False False False 0 Cancel True True True False False False 1 False True end 0 True False True False 300 20 True False 0 Please type the root password for {0}: 10 20 150 20 True False 0 Current root password: 30 60 130 20 True False 0 New root password: 30 110 155 20 True False 0 Re-enter new password: 30 135 250 20 True True False â— False False True True 190 60 250 20 True True False â— False False True True 190 110 250 20 True True False â— False False True True 190 135 400 40 True False 0 Note that if you lose or forget the password, it cannot be recovered. (Remember that passwords are case sensitive) 174 250 20 False 0 The password you typed was incorrect 190 85 True True 1 acceptchangepassword cancelchangepassword False 5 Restore a dump center normal True When you restore a dump of the pool database to the server, you can choose two options: - Reboot: Restore rebooting host after 30 seconds. - Dry run: Restore but don't reboot host. True False 2 True False end Reboot True True True False False False 0 Dry Run True True True False False False 1 Cancel True True True False False False 2 False True end 0 rebootconfirmpoolrestoredb dryrunconfirmpoolrestoredb cancelconfirmpoolrestoredb False 5 Confirm center normal True question Are you sure you want to reboot server? Before that you should shutdown all vms. True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptconfirmreboot cancelconfirmreboot False 5 Confirm center normal True question Are you sure you want to shutdown the server? Ensure that all VMs are already shut down. True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptconfirmshutdown cancelconfirmshutdown False 5 You must detach center normal True error The SR '{0}' is in use on '{1}' and cannot be introduced. You must detach the SR from '{1}' before using it again. True False 2 True False end gtk-close True True True False True False False 0 False True end 0 acceptdetachhbalun False 5 Dettach Storage Repository center normal True warning Detaching this storage repository will make the virtual disks that it contains inaccessible. The contents of the virtual disks themselves will remain intact. If you subsequently reattach the storage repository, you will need to provide the correct device configuration details. Are you sure you want to detach this storage repository? True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 acceptdetachstorage canceldetachstorage False 5 New Virtual Interface Network center normal True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False True False 250 20 True False 0 Select the network location. 25 35 100 20 True False 0 Network: 25 60 400 20 True False listaddnetwork 160 60 320 20 True False 0 Select the MAC Address configuration method. 25 99 Auto-generate 120 20 True True False False 0.49000000953674316 True True 25 130 Manual: 100 20 True True False False True radioauto 25 155 400 20 True False True â— False False True True 160 155 250 20 True False 0 For example; a1:40:58:bf:4c:e0 165 187 500 40 True False 0 Set the maximum transfer rate limit for the virtual network's I/O priority, Quality of Server (QoS) 25 227 50 20 True False 0 Limit: 25 280 130 20 True True â— False False True True 160 280 150 20 True False 0 kbyte/s maximum 313 280 100 20 True False 0 Optional 165 310 True True 1 acceptaddnetwork canceladdnetwork False 5 Delete Virtual Disk center normal True False 2 True False center gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 0 True False 520 20 True False This will delete this virtual disk permanently, destroying the data on it. Continue? 5 True True 1 acceptdeletedisk canceldeletedisk False 5 Delete Network True center dialog True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 40 True False This will delete the selected network permanently. Continue? True True 1 acceptdialogdeletehostnetwork canceldialogdeletehostnetwork False 5 Delete Bond True center dialog True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 40 True False Are you sure that you want to delete this bond? True True 1 acceptdialogdeletehostnic canceldialogdeletehostnic False 5 center normal True warning True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 dialogdeletevdi_accept dialogdeletevdi_cancel False 5 Delete VM center normal True warning Are you sure you want to delete the VM? True False 2 True False end OK True True True False False False 0 Cancel True True True False False False 1 False True end 0 60 True False Delete attached virtual disks 250 20 True True False False True 15 10 Delete associated snapshots 250 20 True True False False True 15 30 True True 2 dialogdelete_accept dialogdelete_cancel False 5 normal True question True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 True False This will remove all alerts from all connected servers. Are you sure you want to continue? True True 2 btdismissallyes btdismissallno False 5 New Virtual Interface Network center normal True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False True False 250 20 True False 0 Select the network location. 25 35 100 20 True False 0 Network: 25 60 400 20 True False listeditnetwork 160 60 320 20 True False 0 Select the MAC Address configuration method. 25 99 Auto-generate 120 20 True True False False 0.49000000953674316 True True 25 130 Manual: 100 20 True True False False True radioeditauto 25 155 400 20 True False True â— False False True True 160 155 250 20 True False 0 For example; a1:40:58:bf:4c:e0 165 187 500 40 True False 0 Set the maximum transfer rate limit for the virtual network's I/O priority, Quality of Server (QoS) 25 227 50 20 True False 0 Limit: 25 280 130 20 True True â— False False True True 160 280 150 20 True False 0 kbyte/s maximum 313 280 100 20 True False 0 Optional 165 310 True True 1 accepteditnetwork canceleditnetwork False 5 License Host center normal True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False True False 450 40 True False To assign a new license to the servers you selected, choose the license edition, enter the license server details, and then click OK. True 10 10 400 130 True False 0 none True False 12 True False True False Citrix XenServer Advanced Edition 250 20 True True False False True True 10 5 Citrix XenServer Enterprise Edition 300 20 True True False False True True advanced 10 30 Citrix XenServer Platinum Edition 300 20 True True False False True True advanced 10 55 Citrix XenServer for XenDesktop 300 20 True True False False True True advanced 10 80 True False <b>License Edition</b> True 15 60 400 100 True False 0 none True False 12 True False True False 350 20 True False 0 Enter the name or IP address of the license server: 5 5 100 20 True False 0 Name: 5 30 100 20 True False 0 Port number: 5 55 200 20 True True â— False False True True 120 30 200 20 True True â— False False True True 120 55 True False <b>License Server</b> True 15 190 True True 1 acceptlicensehost cancellicensehost False 5 Virtual Network Devices Changes center normal True False 2 True False center gtk-ok True True True False True False False 0 False True end 0 True False The virtual network device changes will take efect when you next restart the VM. True True 1 acceptdialognetworkrestart False Options center True False 550 200 True False OpenXenManager can remember the passwords to access your servers. If you enable this option you must specify a master password for security reasons. 550 80 True True False False True 10 220 20 True False False Please specify master password: 80 250 20 True False True False â— False False True True 230 80 280 20 True False Password will be saved on oxc.conf with md5 encryption. 232 111 gtk-apply 100 30 True True True False True 300 140 gtk-cancel 100 30 True True True False True 410 140 600 False 5 Confirm reconfigure center normal True False 2 True False end Reconfigure anyway True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False True False 40 40 True False images/alert.png 10 15 550 80 True False You are reconfiguring the primary management interface. If the new settings are incorrect then XenCenter may permanently lose the connection to the server. You should only proceed if you have verified that these settings are correct. 50 10 True True 1 acceptdialogreconfigure canceldialogreconfigure False 5 Delete Network Interfaces center normal True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 True False 450 40 True False This will delete the selected network interface permanently. Continue? True True 1 acceptremovenetwork cancelremovenetwork False 5 Revert to Snapshot normal True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False True False 32 32 True False gtk-dialog-warning 15 15 400 150 True False 0 0 Are you sure you want to revert to the selected snapshot? Reverting to this snapshot will revert the VM back to the point in the time that the snapshot was created, and the current state of the VM will be lost. Before doing a revert it is recommended take a snapshot of the current state. True 60 15 True True 1 acceptdialogrevert canceldialogrevert False 5 VM snapshot center normal True False 2 True False end OK True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False 400 50 True False 0 0 Are you sure you want to delete this snapshot? This operation cannot be undone. 5 15 True True 1 btacceptsnapshotdelete btcancelsnapshotdelete False 5 VM snapshot center normal True False 2 True False end OK True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False 400 20 True True â— False False True True 5 40 400 20 True False 0 0 New snapshot name: 5 15 True True 1 btacceptsnapshotname btcancelsnapshotname False 5 VM snapshot center normal True False 2 True False end OK True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False 400 20 True True â— False False True True 5 40 400 20 True False 0 0 Enter name for new template: 5 15 True True 1 btacceptsnaptplname btcancelsnaptplname False 5 Warning valid server center normal True warning You must give a valid server to remote log True False 2 True False end OK True True True False False False 0 False True end 0 acceptdialogsyslogempty 800 490 False 5 Properties True center dialog False True False 2 True False end OK True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False True True 200 True True listprop False False 1 horizontal column 40 0 30 4 1 False True 550 True True False True False 500 470 True False 550 450 True False 0 none 600 400 True False 12 100 80 True False True False 90 20 True False 0 Name: 10 25 400 20 True True â— False False True True 110 25 90 20 True False 0 Description: 10 55 90 20 True False 0 Folder: 10 160 400 1 True False 110 160 400 1 True False 110 180 1 21 True False 109 160 1 21 True False 509 160 100 20 True False 0 <None> 120 160 90 20 True False 0 Tags: 10 190 400 1 True False 110 190 400 1 True False 110 360 1 171 True False 110 190 1 171 True False 508 190 100 20 True False 0 Add Tag... 120 203 400 80 True True automatic automatic True False queue True True 110 55 400 26 True False 0.05000000074505806 <b>General</b> True 5 5 25 25 True False images/prop_general.gif 5 5 True False page 1 False True False True False 550 450 True False 0 none True False 12 True False True False Edit Custom Fields... 140 20 True True True False 0 360 510 350 True True automatic automatic True False queue none True False True False 40 400 26 True False 0.079999998211860657 <b>Custom Fields</b> True 5 5 25 25 True False images/prop_customfields.gif 5 5 1 True False page 2 1 False True False True False 550 450 True False 0 none True False 12 True False True False 100 20 True False 0 VM Memory: 10 25 70 20 True True â— False False True True adjpropmem 140 25 20 20 True False MB 210 25 120 20 True False 0 Number of vCPUs: 10 55 70 20 True True â— False False True True adjpropvcpus 140 55 300 20 True False 0 vCPU priority for this virtual machine: 10 95 420 50 True True adjvcpuprio on on 0 False bottom 10 130 80 20 True True â— False False True True adjvcpuprio2 440 130 400 25 True False 0.10000000149011612 <b>CPU and Memory</b> True 5 5 25 25 True False images/prop_cpu.gif 5 5 2 True False page 3 2 False True False True False 550 450 True False 0 none True False 12 True False True False Auto-start on server boot 200 20 True True False False True 5 15 140 20 True False 0 OS Boot Parameters: 5 50 350 20 True True â— False False True True 156 50 100 20 True False 0 Boot Order: 5 80 Move Up 90 20 True False True True False 435 115 Move Down 90 20 True False True True False 435 145 420 250 True True automatic automatic True False queue True True listpropbootorder False False column 2 1 5 115 150 True False <b>Startup Options</b> True 30 10 25 25 True False images/prop_startup.gif 5 5 3 True False page 4 3 False True False True False 550 450 True False 0 none True False 12 True False True False 466 40 True False 0 0 The home server provides the resources for a VM in a pool. XenServer will always attempt to use the resources of the home if it can. 30 Automatically select a home server with available resources (Requires shared storage) 410 40 True True False False True True 80 Use this server as the VM's home server: 300 20 True True False False True True radioautohome 130 450 200 True True automatic automatic True False queue True True listhomeserver False column 1 2 #842184218421 8000 3 5 160 120 True False <b>Home Server</b> True 30 10 25 25 True False images/prop_homeserver.gif 5 5 4 True False page 5 4 False True False True False 100 80 True False TODO 73 145 5 True False page 6 5 False True False True False 25 25 True False images/prop_logdest.png 5 5 550 450 True False 0 none True False 12 True False True False 200 20 True False Server System log destination: 5 15 Local 100 20 True True False False True True 35 55 Remote 100 True True False False 0 0 True True radiologlocal 35 85 60 20 True False False 0 Server: 60 115 300 20 True False True â— False False True True 120 115 250 26 True False 0.20000000298023224 <b>Log destination</b> True 5 5 6 True False page 7 6 False True False True False 550 450 True False 0 none True False 12 True False True False Automatically add this network to new virtual machines. 400 20 True True False False True 5 15 150 True False <b>Network Settings</b> True 30 10 25 25 True False images/prop_networksettings.png 5 5 7 True False page 8 7 False True False True False 550 450 True False 0 none True False 12 True False True False 100 20 True False 0 Size: 20 150 20 True True â— 1 False False True True adjvdisize 3 True 120 20 100 20 True False 0 Location: 60 490 280 True True automatic automatic True False queue True False True listvdilocation False column 1 2 94 20 20 True False GB 275 20 150 True False <b>Size and Location</b> True 30 10 25 25 True False images/prop_sizelocation.png 5 5 8 True False page 9 8 False True False True False 550 450 True False 0 none True False 12 True False True False 100 20 True False 0 Mode: 20 300 20 True False liststgmode 200 20 110 20 True False 0 Device Position: 60 150 20 True False 0 Disk Access Priority: 185 300 20 True False 0 Not implemented by license restrictions 200 185 300 20 True False liststgposition 200 60 380 20 False 0 The selected device position is currently in use or is invalid. 120 90 Bootable 100 20 True True False False True 125 150 True False 0 <b>NAME</b> True 30 10 25 25 True False images/prop_stgvm.png 5 5 9 True False page 10 9 False True False True False 550 450 True False 0 none True False 12 True False True False 520 80 True False 0 0 You can optimize this VM for increased performance in different environments. Use the preset choices to configure it for general use or to enhance Citrix XenApp performance. Alternatively, advanced users can select the manual setting for custom optimization. True 5 5 Optimize for general use 200 20 True True False False True True 15 90 Optimize for Citrix XenApp 200 20 True True False False True True optimizegeneraluse 15 120 Optimize manually (advanced use only) 300 20 True True False False True True optimizegeneraluse 15 150 200 20 True False Shadow memory multiplier: 50 180 100 20 True True â— 4.00 False False True True 258 180 150 True False <b>Advanced Options</b> True 30 10 25 25 True False images/prop_advancedoptions.png 5 5 10 True False page 11 10 False True True True True 1 btvmpropaccept btvmpropcancel 1000 5 1 10 1000 5 1 10 False 5 Backup Server center normal save True filefilterbackupserver True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 savefilebackupserver cancelfilebackupserver False 5 Download Logs center normal save True filefilterbackupserver True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 savefiledownloadlogs cancelfiledownloadlogs False 5 normal save True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfileexportmap cancelfileexportmap False 5 Select License Key center normal filterfilelicensekey True False 2 True False end gtk-open True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 openfilelicensekey cancelfilelicensekey False 5 Select new update file center normal filterfilenewupdate True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfilenewupdate cancelfilenewupdate False 5 center 638 480 normal False False True False 2 True False end gtk-open True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 btopenfile btcancelopenfile False 5 Output XVA file center normal save True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfileoutputxva cancelfileoutputxva False 5 Save Database Pool center normal save filterfilepoolbackupdb True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfilepoolbackupdb cancelfilepoolbackupdb False 5 Restore database Pool center normal True False 2 True False end gtk-open True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfilepoolrestoredb cancelfilepoolrestoredb False 5 Resore Server center normal filefilterrestoreserver True False 2 True False end gtk-open True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 openfilerestoreserver cancelrestoreserver False 5 Save to file center 640 480 normal save True False False True False 1 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 btsavefile btcancelsavefile False 5 Save Status Report center normal save True True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptfilereport cancelfilereport False 5 Force Join Pool center normal True question The hosts in this pool are not homogeneous. Do you want force join to pool? True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 acceptforcejoinpool cancelforcejoinpool False 5 Location center normal True warning Creating a new virtual disk on this LUN will destroy any data present. You must ensure that no other system is using the LUN, including any XenServers, or the virtual disk may become corrupted while in use. Do you wish format the disk? True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 accepformatdisklun cancelformatdisklun False 5 center normal True warning Creating a new virtual disk on this LUN will destroy any data present. You must ensure that no other system is using the LUN, including any XenServers, or the virtual disk may become corrupted while in use. Do you wish to format the disk? True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 acceptformatiscsidisk cancelformatiscsidisk False 5 Host dmesg normal True False 2 True False end gtk-ok True True True False True False False 0 False True end 0 800 520 True True automatic automatic True False queue True True False fill True True 1 accepthostdmesg True False gtk-delete True False gtk-disconnect True False gtk-connect True False gtk-revert-to-saved True False gtk-harddisk True False gtk-missing-image True False 0.47999998927116394 gtk-media-pause True False gtk-media-forward True False 0 0 gtk-media-play True False gtk-media-play True False gtk-connect True False gtk-home True False gtk-cancel True False gtk-add True False gtk-revert-to-saved True False gtk-delete True False gtk-add True False 0.49000000953674316 gtk-info True False gtk-add True False gtk-convert True False gtk-media-stop True False gtk-media-pause True False gtk-disconnect True False gtk-missing-image True False gtk-harddisk True False gtk-media-play True False 0.49000000953674316 gtk-media-stop True False gtk-media-pause True False gtk-refresh True False gtk-copy True False gtk-stop True False gtk-media-record True False gtk-dnd-multiple True False gtk-save-as True False 0 0 gtk-new True False gtk-convert True False gtk-convert True False gtk-new True False gtk-save-as True False gtk-delete True False gtk-save-as True False gtk-home True False images/poolconnected_16.png True False images/poolconnected_16.png True False images/tree_connected_16.png True False images/tree_connected_16.png True False images/poolconnected_16.png True False gtk-save-as True False images/add_server.png True False images/add_storage.png True False images/ok.png True False images/restart.png True False images/shutdown.png True False images/add_vm.png True False images/restart.png True False images/resume.png True False images/shutdown.png True False images/poweron.png True False images/suspend.png 40096 1 10 True False Disks (avg / max KBs) True True False Network (avg / max KBs) True True False No System Alerts True True False Applies to True False Details True False Date True False True False Name True False Description True False NIC True False VLAN True False Auto True False Link Status True False MAC True False NIC True False MAC True False Link Status True False speed True False Duplex True False Vendor True False Device True False PCI Bus True False Name True False Description True False Type True False Shared True False Usage True False Size True False Virtual Alloc True False Name True False Disks (avg / max KBs) True False Network (avg / max KBs) True False Address True False Uptime True False CPU Usage True False Used memory True False Name True False Description True False Size True False VM True False Device True False MAC True False Limit True False Network True False IP Address True False Active True False Name True False Booteable True False Description True False Storage repository True False Pos. True False Size True False Read Only True False Priority True False Active True False Device Path Text String Date and Time Date prueba asdasd images/prop_general.gif <b>General</b> general 0 images/prop_customfields.gif <b>Custom Fields</b> custom 1 images/prop_cpu.gif <b>CPU and Memory</b> cpumemory 2 images/prop_startup.gif <b>Startup Options</b> startup 3 images/prop_homeserver.gif <b>Home Server</b> homeserver 4 images/multipath.png <b>Multipathing</b> multipath 5 images/prop_logdest.png <b>Log destination</b> logdest 6 images/prop_networksettings.png <b>Network Settings</b> networksettings 7 images/prop_sizelocation.png <b>Size and Location</b> sizelocation 8 images/prop_advancedoptions.png <b>Advanced Options</b> advancedoptions 10 RW Read / Write RO Read Only 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1024 768 False 1024 768 True True True False 0 0 etched-out True True automatic True False queue none True False 1000 True False 3 5 True False True True False page 1 False True False page 2 1 False True False page 3 2 False 490 400 False 5 Enter Maintance Mode center normal True False 2 True False center Enter Maintance Mode True True True False False False 0 Cancel 100 True True True False False False 1 False True end 0 True False True False 32 32 True False images/server_alert.png 15 15 400 40 True False 0 This operation will migrate all running VMs from this server and transition it into maintenance mode. 60 15 420 250 True True automatic automatic True False queue True True listmaintenancemode False column 0 1 #c76000000000 2 25 86 220 20 True False 0 Virtual machines on this server: 25 66 True True 1 acceptmaintenancemode cancelmaintenancemode False Enter Master Password True center notification True True False 450 150 True False 400 20 True False Enter the master password to connect your servers: 20 10 64 64 True False images/masterpassword.gif 6 30 120 20 True False Master Password: 70 60 200 20 True True False â— False False True True 200 60 OK 100 20 True False True True False 190 120 Cancel 100 20 True True True False 300 120 250 20 False The password you typed was incorrect. 150 90 True False New VM From Snapshot... True False False image59 False Create Template From Snapshot... True False False image60 False Export Snapshot As Template... True False False image65 False Export Snapshot as VM... True False False image75 False Delete Snapshot True False False False True False Start True False False image32 False True False False Repair Storage True Connect True False False image33 False Disconnect True False False image34 False Forget Saved Password True False False image35 False Remove True False False image36 False Resume True False False image27 False Start On.. True False False image37 False True False True False False Home Server True False Resumen On.. True False False image39 False True False Home Server True False False image40 False True False New VM True False False image41 False Import VM True False False image42 False New Storage Repository True False False image43 False Add To Pool True False False image67 False True False Pools True False False image68 False True False Add Server True False False image44 False Shutdown True False False image45 False Suspend True False False image46 False Pause True False False image47 False Unsuspend True False False image48 False Resume True False False image49 False Reboot True False False image50 False Snapshot True False False image51 False True False True False False Install XenServer Tools True True False Add Server True False False image69 False True False Servers True False False image70 False True False Force Shutdown True False False image52 False Force reboot True False False image53 False Detach Storage True False False image23 False Reattach Storage True False False image24 False Forget Storage True False False image25 False Copy True False False image54 False Export True False False image55 False Convert to template True False False image56 False Delete True False False image57 False Delete True False False image20 False True False False Connect All True True False False Disconnect All True True False True False False Expand All True False False Collapse Children Properties True False False image58 False True False False Migrate to Server True False True False False Home Server True False 800 550 False Management Interface center True False 800 550 True False 770 430 True True 150 True True listmgmtinterfaces False column 1 2 False True True False 620 40 True False True False 40 40 True False images/prop_networksettings.png 100 20 True False Primary 60 10 70 20 True False 0 Network: 10 65 450 20 True False listmgmtnetworks 80 65 600 170 True False 0 in True False 12 True False Automatically obtain IP settings using DHCP 305 20 True True False False True True 10 20 Use the IP settings: 200 20 True True False False True True radiomgmtipdhcp 10 40 90 20 True False 0 IP address: 40 70 400 20 True True â— False False True True 140 70 100 20 True False 0 Subnet mask: 40 95 400 20 True True â— False False True True 140 95 100 20 True False 0 Gateway: 40 120 400 20 True True â— False False True True 140 120 True False <b>IP settings</b> True 10 100 600 140 True False 0 in True False 12 True False Automatically obtain DNS server address using DHCP 450 20 True True False False True True 10 20 Use the following DNS server addresses: 450 20 True True False False True True radiomgmtdnsdhcp 10 40 150 20 True False 0 Preferred DNS server: 40 60 150 20 True False 0 Alternate DNS server: 40 85 350 20 True True â— False False True True 190 60 350 20 True True â— False False True True 190 85 True False <b>DNS Server</b> True 13 280 True True 15 75 OK 80 20 True True True False 610 510 Cancel 80 20 True True True False 700 510 700 40 True False 0 You can configure the primary management interface on server "{0}" here. You can also configure additional management interfaces, for example, for storage or other types of traffic. 10 10 New Interface 100 20 True True True False 12 510 False 5 Migrate Tool center normal True False 2 True False end gtk-help True True True False True False False 0 gtk-ok True True True False True False False 1 gtk-cancel True True True False True False False 2 False True end 0 True False True False 450 100 True False 0 0 This is a tool to aid the migration of xend based Xen virtual machines to XenServer and Xen Cloud Platform. Author: David Markey <david.markey@citrix.com>, Citrix Systems. Please press Help button to read guidelines and considerations 5 5 500 280 True False 0 in True False 12 True False 280 40 True False 0 0 Specify the OSS Xen config file to process <span size="small">(All options except "Output XVA file" will be ignored)</span> True 5 13 200 30 True False 280 12 260 40 True False 0 Add disk in file/block device DISK <span size="small">Make sure first disk given is the boot disk</span> True 5 55 200 30 True False 280 54 480 5 True False 45 240 20 True False 0 Memory 5 90 100 20 True True â— True False False True True adjustment3 True 280 90 100 20 True False 0 Name 5 115 200 20 True True â— Unnamed False False True True 280 115 100 20 True False 0 VCPUS 5 140 100 20 True True â— True False False True True adjustment4 True 280 140 ACPI 80 20 True True False False True True 5 175 APIC 80 20 True True False False True True 95 175 Viridian 80 20 True True False False True True 185 175 PAE 80 20 True True False False True True 275 175 NX 80 20 True True False False True 365 175 HVM VM 100 20 True True False False True True 110 205 PV VM 100 20 True True False False True True radiomigratehvm 210 205 100 20 True False 0 VM Type: 5 205 Attempt sparse mode (detecting chunks that are zero) 450 20 True True False False True 5 230 True False <b>Virtual Machine parameters</b> True 5 125 500 110 True False 0 in True False 12 True False Output XVA file 125 20 True True False False 0 True True 5 20 To selected server 150 20 True True False False True True checkmigrateoutputxva 5 60 250 20 True True False â— unnamed.xva False False True True 200 20 20 20 True True True False right True False gtk-open 453 20 True False <b>Output Options</b> True 7 410 500 40 False True 5 534 True True 1 helpmigratetool acceptmigratetool cancelmigratetool 950 500 False 5 Migrate Tool Help center normal True False 2 True False end gtk-close True True True False True False False 0 False True end 0 True True automatic automatic True False queue True False True False Do a normal export/import of the XVA. Then boot windows with /NOGPLPV. It should in theory then be safe to uninstall the GPLPV paravirtualised drivers. I hadnt any luck with the add/remove programs method so I used the script that is available here: http://meadowcourt.org/downloads/gplpv_uninstall_bat.zip After running this, reboot. Now you will notice a side effect. Your CDROM drive will be missing! To install the guest tools mount the ISO on another machine and copy the files to the new VM over the network. After a reboot you should be running the Citrix paravirtualised drivers, XenMotion should be possible. Linux paravirtualised guests Things to take into consideration when migrating PV Linux guests to XenServer. 1. The most important thing to realise is that XenServer only supports xvda/b/c/d block device names in the guest. That means you cannot have a block device called sda or hda. It also means that block devices cannot be the names of partitions i.e. sda1 or xvda1 will not work. It shouldnt take much effort to make the required changes. An example is supplied further in this document. 2. As of this version, having the guest kernels in the Dom0 is not supported, you MUST use pygrub to bootstrap the guest. In the next version this could possibly be supported 3. XenServer wont allow you to XenMotion your exported VM unless you install XenTools. If your VM has support for the /proc/xen filesystem it should be possible to get these to work on most distros. BEWARE. Live migration is broken in kernels <2.6.31. Use 2.6.32. 4. Grub2 config files arent supported(yet), Neither is ext4 (for /boot) CentOS Migration Example. In this example i'll migrate the CentOS 5.3 x86_64 VM from http://stacklet.com/sites/default/files/centos/centos.5-3.x86-64.20090606.img.tar.bz2 In the stock configuration file you'll see that the first disk block device in set to /dev/sda1. This needs to change to /dev/xvda to work on XenServer. 1. We need to mount the *.img file so we can make the required changes. mount -o loop centos.5-3.x86-64.img /mnt/tmp 2. Edit the /mnt/tmp/boot/grub/grub.conf (menu.lst) file to reference /dev/xvda, i.e. root=/dev/xvda instead of root=/dev/sda1. 3. Edit /mnt/tmp/etc/fstab to reference /dev/xvda instead of /dev/sda1 4. That should be it. Now umount the guest filesystem. umount /mnt/tmp 5. Generate the XVA file. xva.py -n Centos5.3 --is-pv --disk centos.5-3.x86-64.img --filename=centos-5.3.xva ## BUGS ## Expect many bugs. If parsing the guest configuration doesnt work, then supply the parameters manually on the command line. Report bugs to xen-devel or david.markey@citrix.com ## TODO ## Support kernels loaded from the Dom0 Do more sophisticated checks to establish if the VM will run on XenServer/XCP(PV Guests especially), make required changes if possible. Author: David Markey <david.markey@citrix.com> True True 1 closemigratetoolhelp 500 500 False New Network center True False True False 200 20 True False 0 <b>Select the network type:</b> True 10 10 20 20 True True False False True True 50 40 390 40 True False 0 0 <b>External Network</b> Create a network that passes traffic over one of your VLANs True 80 40 20 20 True True False False True True radioexternalnetwork 50 90 390 60 True False 0 0 <b>Internal Network</b> Create a network that does not leave the XenServer host. This can be used as a private connection between VMs. True 80 90 100 20 True False 0 <b>Name:</b> True 10 160 100 20 True False 0 0 <b>Description:</b> True 10 190 330 20 True True â— New Network False False True True 130 160 330 20 True True â— False False True True 130 190 390 20 True False 0 0 Select a physical network interface for the new network: 10 240 100 20 True False 0 <b>NIC:</b> True 10 265 390 20 True False 0 Assign a number to your logical network or VLAN: 10 300 100 20 True False 0 0 <b>VLAN:</b> True 10 330 60 20 True True â— False False True True adjustment2 130 330 260 20 False The specified VLAN tag is already in use! 200 330 Automatically add this network to new virtual machines. 390 20 True True False False True 10 370 480 60 True False Select this check box to add this network to any new virtual machine (VM) created using New VM Wizard. You will be ableto remove this network when creating new VMs using the New VM Wizard 30 390 24 24 True False images/prop_networksettings.png 461 15 Create 80 20 True True True False 320 460 Cancel 80 20 True True True False 410 460 330 20 False listnetworknic False False 1 False False False True True 130 400 330 20 True False listnetworknic 130 265 False New Pool center True False 600 550 True False 400 280 True True listpoolvms False column 4 2 column 4 1 #a94aa94aa94a 4 3 140 210 700 70 True False True False 100 80 True False images/add_vm.png 570 70 True False 0 Enter a name and description for the new pool and choose members for the new pool. 108 100 20 True False 0 Name: 30 85 100 20 True False 0 Description: 30 110 400 20 True True â— New Pool False False True True 140 85 400 20 True True â— False False True True 140 110 100 20 True False 0 Select master: 30 150 400 20 True False listpoolmaster 140 150 120 20 True False 0 Select members: 30 182 Finish 100 25 True True True False 350 515 Cancel 100 25 True True True False 460 515 False 5 New Storage Repository center normal True warning Warning: you must ensure that the SR is not in use by any server not connected to XenCenter. Failure to do so may result in data loss. SR: {0} Do you want to reattach the SR? True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptnewstgreattachaoe cancelnewstgreattachaoe False 5 New Storage Repository center normal True warning Warning: you must ensure that the SR is not in use by any server not connected to XenCenter. Failure to do so may result in data loss. SR: {0} Do you want to reattach the SR? True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptnewstgreattachnfs cancelnewstgreattachnfs False 5 New Storage Repository center normal True False 2 True False end < Previous True False True True False False False 0 Next > True False True True False False False 1 Finish True False True True False False False 2 Cancel True True True False False False 3 False True end 0 True False True False 800 500 True False True False True False 0 in True False 12 50 True False 32 32 True False images/add_storage.png 10 10 400 20 True False 0 <b>Choose the type of new Storage</b> True 75 15 False True 0 True False True False 0 in True False 150 True False 150 20 True False 150 20 True False 0 <b> Type</b> True 35 150 20 True False 150 20 True False 0 <b> Location</b> True 60 False True 0 True False True False 0 in True False True False True False False True False True False 150 20 True False 0 Virtual disk storage 15 20 NFS VHD 100 20 True True False False True True 25 45 iSCSI 100 20 True True False False True True radionewstgnfsvhd 25 70 Hardware HBA 120 20 True True False False True True radionewstgnfsvhd 25 95 255 20 True False 0 Advanced StorageLink technology 20 135 NetApp 100 20 True True False False True True radionewstgnfsvhd 25 160 Dell EqualLogic 120 20 True True False False True True radionewstgnfsvhd 25 185 150 20 True False 0 ISO library 20 225 Windows File Sharing (CIFS) 210 20 True True False False True True radionewstgnfsvhd 25 250 NFS ISO 180 20 True True False False True True radionewstgnfsvhd 25 275 320 350 True False 0 0 NFS servers are a common form of shared filesystem infrastructure, and can be used as a storage repository substrate for virtual disks. As NFS storage repositories are shared, the virtual disks stored in them allow VMs to be started on any server in a resource pool and to be migrated between them using XenMotion. When you configure an NFS storage repository, you simply provide the hostname or IP address of the NFS server and the path to a directory that will be used to contain the storage repository. The NFS server must be configured to export the specified path to all servers in the pool True 305 20 100 20 True False <b>Experimental</b> True 20 315 ATA over Ethernet (AoE) 200 20 True True False False True True radionewstgnfsvhd 25 340 True False page 1 False True False True False 100 20 True False 0 Name: 20 20 350 20 True True â— NFS virtual disk storage False False True True 160 20 100 20 True False 0 Share Name: 20 50 350 20 True True â— False False True True 160 50 150 20 True False Example: server:/path 160 75 120 20 True False 0 Advanced Options: 20 115 350 20 True True â— False False True True 160 115 Scan 80 20 True False True True False 520 50 Create a new SR 200 20 True True False False True True 20 155 Reattach an existing SR: 200 20 True False True False False True True radiocreatenewsr 20 185 490 150 True True automatic automatic True False queue True False True listreattachnewstgnfs False column 1 20 213 1 True False page 3 1 False True False True False 100 20 True False 0 Name: 50 20 350 20 True True â— iSCSI virtual disk storage False False True True 150 20 100 20 True False 0 Target Host: 50 45 260 20 True True â— False False True True 150 45 5 20 True False : 420 45 65 20 True True â— 3260 False False True True 435 45 Use CHAP 100 20 True True False False True 45 80 500 110 True False False 0 in True False 12 True False 80 20 True False 0 CHAP User: 20 350 20 True True â— False False True True 95 20 85 20 True False 0 CHAP Secret: 55 350 20 True True â— False False True True 95 55 True False CHAP Authentication True 40 110 100 20 True False 0 Target IQN: 40 245 100 20 True False 0 Target LUN: 40 275 350 20 True False listtargetiqn 150 245 350 20 True False listtargetlun 150 275 Discover IQNs 120 20 True False True True False 510 245 Discover LUNs 120 20 True False True True False 510 275 2 True False page 4 2 False True False True False 100 20 True False 0 Name: 10 20 450 20 True True â— Hardware HBA virtual disk storage False False True True 110 20 350 20 True False 0 Choose a LUN to re-attach or create new SR on: 10 50 550 250 True True automatic automatic True False queue 100 80 True True listhbalun False column 1 0 14 85 3 True False page 7 3 False True False True False 100 20 True False 0 Name: 15 15 450 20 True True â— False False True True 115 15 100 20 True False 0 Share Name: 15 45 450 20 True True â— False False True True 115 45 200 20 True False 0 Example: \\server\sharename 115 75 600 230 True False 0 in True False 12 True False Use different user name 200 20 True True False False True 10 20 400 100 True False 0 in True False 12 True False 100 20 True False 0 User Name: 10 10 100 20 True False 0 Password: 10 40 250 20 True True â— False False True True 120 10 250 20 True True â— False False True True 120 40 True False <b>Login</b> True 10 50 140 20 True False 0 Advanced Options: 10 165 350 20 True True â— False False True True 150 165 True False <b>Server Options</b> True 15 110 4 True False page 5 4 False True False True False 100 20 True False 0 Name: 15 15 450 20 True True â— False False True True 170 15 100 20 True False 0 Share Name: 15 45 450 20 True True â— False False True True 170 45 200 20 True False 0 Example: server:/path 170 75 125 20 True False 0 Advanced Options: 15 115 450 20 True True â— False False True True 170 115 5 True False page 6 5 False True False True False 100 20 True False 0 Name: 20 20 350 20 True True â— False False True True 160 20 100 20 True False 0 Device 20 50 350 20 True True â— /dev/etherd/e False False True True 160 50 300 40 True False 0 Use command aoe-stat to fill this field: Example: /dev/etherd/e1.1 162 79 Create a new SR 200 20 True True False False True True 20 155 Reattach an existing SR: 200 20 True False True False False True radiocreatenewaoe 20 185 Scan 80 20 True True True False 520 50 490 150 True True automatic automatic 0 True False queue True True listreattachnewstgaoe False False column 1 20 213 6 True False page 6 5 False True True 1 True True 1 True True 1 previousnewstorage nextnewstorage finishnewstorage cancelnewstorage False 5 Disk Settings 400 320 normal True False 2 True False end gtk-apply True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False 400 20 True False 0 Enter the settings for the new virtual disk 20 25 80 20 True True â— False False True True disksize 2 True 56 50 40 20 True False 0 Size: 20 50 20 20 True False GB 145 50 100 20 True False 0 Location: 20 100 400 200 True True automatic automatic True False queue True True listnewvmdisk False 0 Name 0 Description 1 Size (GB) 2 Free Space (GB) 3 20 130 True True 1 acceptnewvmdisk cancelnewvmdisk 20 1 1 False 5 normal True warning An existing SR was found on the selected LUN. Click Reattach to use the existing SR, or click Format to destroy any data present on the disk and create a new SR. Warning: to prevent data loss you must ensure that the LUN is not in use by any other system, including XenServer hosts that are not connected to XenCenter. '{0}' True False 2 True False end Reattach True True True False False False 0 Format True True True False False False 1 Cancel True True True False False False 2 False True end 0 acceptareattachformathbalun acceptbreattachformathbalun cancelreattachformathbalun False 5 center normal True warning An existing SR was found on the selected LUN. Click Reattach to use the existing SR, or click Format to destroy any data present on the disk and create a new SR. Warning: to prevent data loss you must ensure that the LUN is not in use by any other system, including XenServer hosts that are not connected to XenCenter. True False 2 True False end Reattach True True True False False False 0 Format True True True False False False 1 Cancel True True True False False False 2 False True end 0 reattachscsidisk formatscsidisk cancelreattachscsidisk False 5 Reattach storage center normal True warning Warning: to prevent data loss you must ensure that the LUN is not in use by any other system, including XenServer hosts that are not connected to XenCenter. SR: '{0}' Do you want to reattach the SR? True False 2 True False end gtk-yes True True True False True False False 0 gtk-no True True True False True False False 1 False True end 0 acceptreattachhbalun cancelreattachhbalun False 5 Confirm True center normal True True warning Remove the server {0} from the pool {1}? WARNING: This will permanently delete and reinitialize all local storage. Your data will be lost, and the server will be rebooted. True True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptremoveserverfrompool cancelremoveserverfrompool False 5 Repair Storage Repository center normal True False 2 True False end Repair True True True False False False 0 Cancel True True True False False False 1 False True end 0 True False 32 32 True False images/repair.png 10 10 450 80 True False 0 0 This operation will attempt to repair the broken storage you selected. If this operation cannot repair the storage, it will give you a more detailed error message. 60 10 100 20 True False Current Status: 10 80 490 200 True True automatic automatic True False queue True False listrepairstorage False True 250 column 1 2 True column True 1 right 3 10 105 True True 1 True False <span foreground='red'><b>Host could not be contacted</b></span> True True True 2 acceptrepairstorage cancelrepairstorage False 5 Save screenshot center normal save True False 2 True False end gtk-save True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 acceptsavescreenshot cancelsavescreenshot False 5 System Status Report center normal True False 2 True False end gtk-ok True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False True False 32 32 True False images/report.png 5 10 450 20 True False <b>Select the data you wish to include in your report</b> True 50 15 150 20 True False 0 Report Content Item 15 60 150 20 True False 0 Confidentiality Rating 280 60 400 350 True True automatic automatic True False queue True False True True listreport False 25 column 1 fixed 330 column 2 25 column 3 15 90 Citrix Privacy Statement 180 20 True True True False none False 0 http://citrix.com/site/aboutCitrix/legal/secondLevel.asp?level2ID=2208 450 60 250 True False True False 100 20 True False 0 <b>Description</b> True 220 True False 0 0 Memory dump created on server crash, if you select this option you will be prompted for these files to be removed after the report has been compiled True 15 20 True True 0 True False 100 20 True False 0 <b>Size</b> True 20 True False 0 0 B 15 20 True True 1 True False 100 20 True False 0 <b>Time</b> True 20 True False 0 Negligible 15 20 True True 2 True False 120 20 True False 0 <b>Confidentiality</b> True 220 80 True False 0 0 These files will contain personally identifiable information True 15 20 True True 3 450 100 Clear All 100 20 True True True False 200 450 Select All 100 20 True True True False 310 450 250 5 True False 450 400 100 20 True False 0 <b>Total Size</b> True 460 415 100 20 True False 0 <b>Total time</b> True 460 440 100 20 True False 0 label 600 415 100 20 True False 0 label 600 440 True True 1 acceptstatusreport cancelstatusreport column False 5 Update Manager center normal True False 2 True False end gtk-close True True True False True False False 0 False True end 0 True False 650 True False 32 32 True False images/patch.png 20 20 400 20 True False 0 View details of installed updates and install new updates True 75 28 600 220 True False 0 none True False 12 True True automatic automatic True False queue True True listupdates False column 1 True False <b>Updates</b> True 20 72 290 200 True False 0 none True False 12 True True automatic automatic True False queue True False True False True False True False 50 20 True False 0 Name: 10 160 True False 0 0 True 90 10 True True 0 True False 100 20 True False 0 Description: 10 150 True False 0 0 True 90 10 True True 1 True False 100 20 True False 0 Version: 10 150 True False 0 0 90 10 True True 2 True False 100 20 True False 0 Guidance: 10 150 True False 0 0 90 10 True True 3 True False <b>Properties</b> True 20 320 290 180 True False 0 none True False 12 True True automatic automatic True False queue True False True True listupdatestatus False column 1 True False <b>Status</b> True 330 320 Upload New Update 150 20 True True True False 470 58 Apply Patch 100 20 True False True True False 520 500 Remove 100 20 True True True False 517 296 True True 1 closeupdatemanager False 5 Disk Settings 400 320 normal True False 2 True False end gtk-apply True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False 80 20 True True â— False False True True disksize2 2 True 180 70 40 20 True False 0 Size: 75 70 20 20 True False GB 270 70 350 20 True False 0 Select a storage repository to create the disk on 20 100 80 20 True False 0 Description: 75 45 335 20 True True â— False False True True 180 45 100 20 True False 0 Name: 75 20 335 20 True True â— False False True True 180 20 40 40 True False gtk-add 20 25 500 200 True True automatic automatic True False queue True True listnewvmdisk1 False 0 Name 0 Description 1 Size (GB) 2 Free Space (GB) 3 20 130 True True 1 acceptvmaddnewdisk cancelvmaddnewdisk False Attach Disk True False 435 True False 300 20 True False 0 Select a disk to add from the list below: 15 20 400 150 True True automatic automatic True False queue True True listattachdisk False False 1 column 0 4 2 #525250505050 0.90000000000000002 4 3 15 50 Attach as read-only 150 40 True True False False none 0 0 True 30 210 Attach 80 25 True False True True False 245 215 Cancel 80 25 True True True False 335 215 False OpenXenManager center dialogvmprop True False 300 100 True False 250 20 True False Scanning for free devices 25 39 250 20 True False 25 75 False 5 Import VM center normal True False 2 True False end < Previous True False True True False False False 0 Next > True False True True False False False 1 Finish True False True True False False False 2 Cancel True True True False False False 3 False True end 0 True False True False 800 500 True False True False True False 0 in True False 12 50 True False 32 32 True False images/import.png 10 10 400 20 True False 0 <b>Specify the location and type of the import source</b> True 75 15 False True 0 True False True False 0 in True False 150 True False 150 20 True False 150 20 True False 0 <b> Import Source</b> True 35 150 20 True False 150 20 True False 0 <b> Home server</b> True 60 150 20 True False 150 20 True False 0 <b> Storage</b> True 85 150 20 True False 150 20 True False 0 <b> Network</b> True 110 150 20 True False 100 20 True False 0 <b> Finish</b> True 135 False True 0 True False True False 0 in True False True False True False False True False True False 120 20 True False 0 Import File Name: 20 25 450 30 True False filefilterimportvm 150 20 100 20 True False 0 Import Type: 20 70 Exported VM 120 20 True True False False True True 45 100 Exported Template 150 20 True True False False True True radioexportedvm 45 125 600 20 True False 0 Note: The same file extension (*.xva) is used for both exported VMs and exported templates. 20 159 True False page 1 False True False True False True False Click on a server to nominate it as the home server for the imported VM or for any new VMs to be based on the imported template. The home server will be used by default to start up the VM and to provide resources such as local storage. 20 20 600 40 True False 0 Click on a pool if you do not want to nominate a home server: the most suitable available server will be used 20 90 600 200 True True automatic automatic True False queue True True listimportservers False column 2 0 2 1 20 145 1 True False page 2 1 False True False True False 600 20 True False 0 Select a storage repository where virtual disks for the new VM/template will be stored 20 24 600 300 True True automatic automatic True False queue True True listimportstg False column 0 2 #98da9ad091ff 3 20 50 2 True False page 3 2 False True False True False 550 20 True False 0 The virtual network interfaces configured for the imported VM are listed below. 20 20 450 20 True False 0 You can add, modify or remove virtual network interfaces as required. 20 40 550 250 True True automatic automatic True False queue True True listimportnetworks False 0 Name 0 70 MAC Address 150 1 Network True False 0 2 20 86 Add 80 20 True True True False 410 345 Delete 80 20 True True True False 495 345 3 True False page 4 3 False True False True False 500 20 True False 0 Click the Finish button to complete the import process and close the wizard. 20 25 350 20 True False 0 The import process may take several minutes. 20 50 600 160 True False 0 in True False True False True False 600 20 True False 0 To have your new VM start up as soon as the import process is complete select this checkbox. 5 24 Start VM after import 200 20 True True False False True 5 100 True False <b>Automatically start new VM</b> True 20 200 4 True False page 5 4 False True True 1 True True 1 True True 1 previousvmimport nextvmimport finishvmimport cancelvmimport False popup True center 300 120 True False True False Close 100 20 True True True False 250 80 100 80 True False gtk-dialog-warning 250 True False 0 0 label True 112 10 150 False True True 5 True center 300 tooltip True warning True True False 2 True False end Close True True True False False False 0 False True end 0 closewbutton False 5 Install License Key center normal True warning There was an error processing your license. Please contact your support representative. Check your settings and try again. True False 2 True False end gtk-close True True True False True False False 0 False True end 0 closewarninglicense False 5 Custom Fields center normal True False 2 True False end gtk-apply True True True False True False False 0 gtk-cancel True True True False True False False 1 False True end 0 True False 350 100 True False 0 none True False 12 True False 50 20 True False 0 Name: 250 20 True True â— False False True True 75 60 20 True False 0 Type: 30 250 20 True False listcombocustomfields 75 30 Add 60 20 True True True False 55 True False <b>Add</b> True 9 15 350 250 True False 0 none True False 12 True False 215 230 True True automatic automatic True False queue True True listcustomfields False column 0 Delete 100 20 True True True False 225 True False <b>Current custom fields</b> True 12 110 True True 1 acceptwcustomfields cancelwcustomfields 1024 True False OpenXenManager center 768 668 1024 True True True True False True False False _File True True False True False False Exit True True False False _View True True False True False False Server View True True False True False False XenServer templates True True False False Custom templates True True False False Local Storage True True False False Hidden VMs True True False True False False Toolbar True True False False _Pool True True False True False False New Pool... True True False True False False Properties True True False True False False Add Server True True False True False False Servers True True False True False False Remove Server True True False True False False Delete True True False False Disconnect True True False True False False Backup Database True True False False Restore Database True True False False _Server True True False True False False New VM... True True False True False False Properties True True False False Management Interfaces True True False False Show dmesg True True False True False False Reboot True True False False Shutdown True True False True False False Add... True True False False Connect True True False False Connect all True True False False Forget saved Password True True False False Disconnect True True False False Disconnect All True True False False Remove True True False False Add To Pool True True False Pools True False False image71 False True False True False True False False Assign License Key... True True False False Change Server Password... True True False True False False Backup server True True False False Restore server True True False False Download Logs True True False False Enter Maintenance Mode True True False False Exit Maintance Mode True True False False _VM True True False True False False New... True True False False Import... True True False True False False Properties True True False True False False Start True True False False Suspend True True False False Reboot True True False False Shut Down True True False False Take snapshot True True False True False False Copy VM True True False False Export as Backup... True True False False Convert to template True True False True False False Install XenServer Tools True True False False Start in Recovery Mode True True False True False False Force Shutdown True True False False Force Reboot True True False True False False Delete True True False False _Storage True True False True False False New Storage Repository... True True False True False False Properties True True False True False False Set as Default Storage Repository True True False True False False New Virtual Disk... True True False False Attach Virtual Disk... True True False False _Templates True True False True False False New VM... True True False False Import Template... True True False False Export Template... True True False False Copy Template... True True False False Delete Template True True False False T_ools True True False True False False Get Server Status Report... True True False False System Alerts... True True False True False False Options True True False True False False Updates Manager... True True False True False False Send Ctrl-Alt-Del True True False False Take Screenshot True True False False Migrate Tool True True False False Window True True False True False False Log Window True True False False Help True True False True False False Check for Updates True True False False XenServer on the Web True True False gtk-about True False False True True accelgroup1 False True 1024 True True True False True False both-horiz True False False True Add New Server True imageaddserver False True True False False True False False True New Storage True imageaddstorage False True True False False True False False True New VM True imagenewvm False True True False False True False False True Start True imagestart False True True False False True Shut Down True imageshutdown False True False False True Force Shutdown True imageforceshutdown False True True False False True Reboot True imagerestart False True False False True Force Reboot True imageforcereboot False True True False False True Suspend True imagesuspend False True True False False True Resume True imageresume False True True False False True False False True True lbltbalerts imagealerts False True False True True True True True True True True True â— False False True True False False True True automatic automatic True False queue 250 True True listvm False False column True 0 225 3 center 1 False 2 False 4 False 3 False 5 False 6 False 7 False 8 True True True True True True 25 True False 16 16 True False gtk-missing-image 9 4 400 16 True False 0.0099999997764825821 8 label True 71 4 False False True True False 0 none True False 12 True True automatic automatic True False queue none True False True False Properties 100 20 True True True False 530 15 True False True True True True True 10 True False 100 20 True False 0 0 Name: 100 20 True False 0 0 Description: 25 100 20 True False 0 0 Tags: 50 100 20 True False 0 0 Folder: 75 100 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 200 20 True False 0 0 label True 200 50 100 20 True False 0 0 label True 200 75 True False General True True 5 0 True True True True True 10 True False True False 100 20 True False 0 0 Fully applied: True False 0 0 label True 200 True True 0 True False 150 20 True False 0 0 Partially Applied: True False 0 0 label True 200 True True 1 True False Updates True True 5 1 20 40 True False <b>Pool General Properties</b> True True False General False False 0 none True False 12 True True automatic automatic True False queue none True False True False Properties 100 20 True True True False 530 15 True False True True True True True 10 True False 100 20 True False 0 0 Name: 100 20 True False 0 0 Description: 25 100 20 True False 0 0 Tags: 50 100 20 True False 0 0 Folder: 75 100 20 True False 0 0 Type: 100 100 20 True False 0 0 Size: 125 100 20 True False 0 0 SCSI ID: 150 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 20 True False 0 0 label True 200 50 20 True False 0 0 label True 200 75 20 True False 0 0 label True 200 100 20 True False 0 0 label True 200 125 20 True False 0 0 label True 200 150 True False General True True 5 0 True True True True True 10 True False 100 20 True False 0 0 State: 100 20 True False 0 0 xenAtlantis: 25 100 20 True False 0 0 label True True 200 20 True False 0 0 label True True 200 20 True False Status True True 5 1 True True True True True 10 True False 150 20 True False 0 0 Multipath capable: 20 True False 0 0 label True True 200 True False Multipathing True True 5 2 20 40 True False <b>Storage General Properties</b> True 1 True False General 1 False False 0 none True False 12 True True automatic automatic True False queue none True False True False Properties 100 20 True True True False 550 15 True False True True True True True 10 True False 100 20 True False 0 0 Name: 100 20 True False 0 0 Description: 25 100 20 True False 0 0 Tags: 50 100 20 True False 0 0 Folder: 75 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 100 20 True False 0 0 label True 200 50 20 True False 0 0 label True 200 75 True False General True True 0 True True True True True 10 True False 100 20 True False 0 0 Memory: 100 20 True False 0 0 Virtual CPUs: 25 100 20 True False 0 0 VCPU priority: 50 100 20 True False 0 0 label True True 200 100 20 True False 0 0 label True True 200 25 20 True False 0 0 label True True 200 50 True False Memory and VCPUs True True 5 1 True True True True True 5 True False 100 20 True False 0 0 Auto-boot: 20 True False 0 0 label True 200 150 20 True False 0 0 OS boot parameters: 25 20 True False 0 0 label True 200 25 True False Boot Options True True 10 2 20 40 True False <b>Template General Properties</b> True 2 True False General 2 False True False 0 none True False 12 True False True True automatic automatic True False queue none True False True False Add Disk... 100 20 True True True False 400 5 Properties 100 20 True False True True False 510 5 Remove 100 20 True False True True False 620 5 750 450 True True automatic automatic True False queue True True liststg False 0 150 Name lbltreestg1 1 200 Description lbltreestg2 2 200 Size lbltreestg3 3 150 VM lbltreestg4 4 15 40 Rescan 100 20 True True True False 15 5 True False <b>Virtual Disks</b> True 3 True False Storage 3 False False 0 none True False 12 True True automatic automatic True False queue none True True listsearch True True 1 Name lbltreesearch1 True True 6 1 0 10 0 1 2 10 1 autosize Disks lbltreesearch2 True True 10 6 Network lbltreesearch3 True True 10 7 Address lbltreesearch4 True True 10 8 Uptime lbltreesearch5 True True 10 9 True False <b>Overview</b> True 4 True False Search 4 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False Properties 120 20 True True True False 550 14 True False True True True True True 5 True False 100 20 True False 0 0 Name: 20 True False 0 0 label True 200 100 20 True False 0 0 Description: 25 20 True False 0 0 label True 200 24 100 20 True False 0 0 UUID: 50 100 20 True False 0 0 Tags: 75 100 20 True False 0 0 Folder: 100 150 20 True False 0 0 Operating System: 125 150 20 True False 0 0 Virtualization State: 150 150 20 True False 0 0 Time since startup: 175 20 True False 0 0 label True 200 50 20 True False 0 0 label True 200 75 20 True False 0 0 label True 200 100 20 True False 0 0 label True 200 125 20 True False 0 0 label True True 200 150 20 True False 0 0 label True 200 175 True False General True True 0 True True True True True 5 True False 100 20 True False 0 0 Memory: 100 20 True False 0 0 Virtual CPUs: 25 100 20 True False 0 0 VCPU priority: 50 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 20 True False 0 0 label True 200 50 True False Memory and VCPUs True True 10 1 True True True True True 5 True False 100 20 True False 0 0 Auto-boot: 150 20 True False 0 0 OS boot parameters: 25 20 True False 0 0 label True 200 True False 0 0 label True 200 25 True False Memory and VCPUs True True 10 2 20 40 True False <b>VM General properties</b> True 5 True False General 5 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False Properties 100 20 True True True False 550 10 True False True True True True True 5 True False 100 20 True False 0 0 Name: 100 20 True False 0 0 Description: 25 100 20 True False 0 0 Tags: 50 100 20 True False 0 0 Folder: 75 100 20 True False 0 0 iSCSI IQN: 100 100 20 True False 0 0 Pool master: 125 120 20 True False 0 0 Log destination: 150 100 20 True False 0 0 Server uptime: 175 120 20 True False 0 0 Toolstack uptime: 200 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 20 True False 0 0 label True 200 50 20 True False 0 0 label True 200 75 20 True False 0 0 label True 200 100 20 True False 0 0 label True 200 125 20 True False 0 0 label True 200 150 20 True False 0 0 label True 200 175 20 True False 0 0 label True 200 200 True False General True True 0 True True True True True 5 True False 120 20 True False 0 0 DNS hostname: 20 True False 0 0 Primary: 25 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 True False Management Interface True True 10 1 True True True True True 5 True False 100 20 True False 0 0 Server: 100 20 True False 0 0 Xen: 25 20 True False 0 0 VMs: 50 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 True False 0 0 label True 200 50 True False Memory True True 10 2 True True True True True 10 True False 150 20 True False 0 0 XenServer build date: 175 20 True False 0 0 XenServer build number: 25 150 20 True False 0 0 XenServer version: 50 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 20 True False 0 0 label True 200 50 True False Version Details True True 5 3 True True True True 5 True False 100 20 True False 0 0 Expiry date: 120 20 True False 0 0 Server License: 25 100 20 True False 0 0 Product code: 50 100 20 True False 0 0 Serial number: 75 20 True False 0 0 label True 200 20 True False 0 0 label True 200 25 20 True False 0 0 label True 200 50 20 True False 0 0 label True 200 75 True False License Details True True 10 4 True True True True True 5 True False 100 20 True False 0 0 CPU: True False 0 0 label True 200 True False CPUs True True 10 5 True True True True True 5 True False 100 20 True False 0 0 Applied: True False 0 0 label True 200 True False Updates True True 10 6 20 40 True False <b>Server General Properties</b> True 6 True False General 6 False True False 0 none True False 12 True False True True False False True False True False 400 150 True False 0 none True False 12 True False 150 20 True False 0 Minimum Memory: 5 10 150 20 True False 0 Maximum Memory: 5 35 150 20 True False 0 Static Maximum: 5 60 60 20 True True â— False False True True 200 10 60 20 True True â— False False True True 200 35 60 20 True True â— False False True True 200 60 20 20 True False 0 MB 265 10 20 20 True False MB 265 35 20 20 True False MB 265 60 300 20 True False * Static Maximum can only be changed on halted VMs 10 85 Apply 80 20 True True True False 200 110 True False <b>Set Memory</b> True 15 158 400 120 True False 0 none True False 12 True False 150 20 True False 0 Minimum Memory: 5 10 120 20 True False 0 Maximum Memory: 5 35 120 20 True False 0 Static Maximum: 5 60 100 20 True False 0 label 200 10 100 20 True False 0 label 200 35 100 20 True False 0 label 200 60 True False True 15 15 True False page 1 False True False True False 400 120 True False 0 none True False 12 True False 150 20 True False 0 Minimum Memory: 5 10 120 20 True False 0 Maximum Memory: 5 35 100 20 True False 0 label 200 10 100 20 True False 0 label 200 35 True False True 15 15 400 250 True False 0 none True False 12 True False Apply 80 20 True True True False 200 200 Set a fixed memory of: 200 20 True True False False True True 5 10 100 20 True False 0 Memory: 30 40 60 20 True True â— False False True True 200 40 20 20 True False MB 270 40 Automatically allocate memory within this range: 350 20 True True False False True radiomemstatic 5 80 120 20 True False 0 Minimum Memory: 30 110 120 20 True False 0 Maximum Memory: 30 140 60 20 True True â— False False True True 200 115 60 20 True True â— False False True True 200 140 20 20 True False MB 270 115 20 20 True False MB 270 140 320 40 True False Important: In order to automatically allocate memory XenServer Tools needs to be installed. 18 160 True False <b>Set Memory</b> True 15 158 1 True False page 2 1 False True False <b>Memory</b> True 7 True False Memory 7 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False True False <b>Hardware Info</b> True 8 True False Hardware 8 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False 70 20 True False 0 0 DVD drive: 16 30 500 20 True False listvmstoragedvd 2 3 0 138 30 Add... 80 20 True True True False 250 Attach... 90 20 True True True False 335 5 20 True False 430 Properties 90 20 True False True True False 440 Deactivate 90 20 True False True True False 535 5 20 True False 630 Detach 80 20 True False True True False 640 Delete 80 20 True False True True False 725 800 350 True True automatic automatic True False queue True True listvmstorage False 0 True Name lbltreevmstorage1 0 True Description lbltreevmstorage2 1 True Storage repository lbltreevmstorage3 2 True Pos. lbltreevmstorage4 3 True Size lbltreevmstorage5 4 True Read Only lbltreevmstorage6 0.019999999552965164 5 True Priority lbltreevmstorage7 6 True Active lbltreevmstorage8 7 True Device Path lbltreevmstorage9 8 Bootable lbltreevmstorage10 11 16 80 True False <b>Virtual disks</b> True 9 True False Storage 9 False True False 0 none True False 12 True False True True automatic automatic True False queue none True False True False 755 350 True True automatic automatic True False queue True True listhoststorage False 0 True Name lbltreehoststorage1 0 True Description lbltreehoststorage2 1 True Type lbltreehoststorage3 2 True Shared lbltreehoststorage4 3 True Usage lbltreehoststorage5 4 True Size lbltreehoststorage6 0.019999999552965164 5 True Virtual Alloc lbltreehoststorage7 6 1 31 True False <b>Storage repositories</b> True 10 True False Storage 10 False True False 0 none True True automatic automatic True False queue none True False True False 750 350 True True automatic automatic True False queue True True listhostnics False 0 True True NIC lbltreehostnics1 0 True MAC lbltreehostnics2 1 True Link Status lbltreehostnics3 2 True Speed lbltreehostnics4 3 True Duplex lbltreehostnics5 4 True Vendor lbltreehostnics6 0.019999999552965164 5 True Device lbltreehostnics7 6 PCI Bus lbltreehostnics8 7 1 31 Delete Bond 100 20 True True True False 650 Add Bond... 100 20 True True True False 545 True False <b>Network Interfaces cards</b> True 11 True False NICs 11 False True False 0 none True False 12 True False True True automatic automatic True False queue none True False True False 790 350 True True automatic automatic True False queue True True listhostnetwork False 0 True 150 Name lbltreehostnetwork1 0 True 200 Description lbltreehostnetwork2 1 True 50 NIC lbltreehostnetwork3 2 True 50 VLAN lbltreehostnetwork4 3 True 50 Auto lbltreehostnetwork5 4 True 50 Link Status lbltreehostnetwork6 5 MAC lbltreehostnetwork7 6 1 31 Properties 100 20 True True True False 555 Add Network... 120 20 True True True False 430 Remove Network 130 20 True True True False 660 True False <b>Server networks</b> True 12 True False Network 12 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False Add Interface... 120 20 True True True False 460 Properties 100 20 True False True True False 585 Remove Interface 120 20 True False True True False 690 790 350 True True automatic automatic True False queue True True listvmnetwork False 0 True Device lbltreevmnetwork1 0 True 200 MAC lbltreevmnetwork2 1 True Limit lbltreevmnetwork3 2 True 200 Network lbltreevmnetwork4 3 True 100 IP Address lbltreevmnetwork5 4 True Active lbltreevmnetwork6 5 16 30 True False <b>Virtual Network Interfaces</b> True 13 True False Network 13 False True False 0 none True False 12 True False 25 True False Send Ctrl-Alt-Del 20 True False True False 0 5 Enter fullscreen 20 True False False False 0 140 5 Copy selected text 130 20 True True True False 270 5 Undock 100 20 True True True False 420 5 False True 0 True True True True automatic automatic True True True True queue True True True True True True True GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK 0 0 True True 1 True False <b>Virtual console</b> True 14 True False Console 14 False True False 0 none True False 12 True False True True automatic automatic True False queue none True False True False 700 280 True True automatic automatic bottom-right True False GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK all queue none 10 50 700 280 True True automatic bottom-right True False GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK queue none 10 405 700 280 True True automatic bottom-right True False GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK queue none 10 710 100 20 True False 0 <b>CPU Usage</b> True 300 30 130 20 True False 0 <b>Memory Usage</b> True 300 380 120 20 True False 0 <b>Network Usage</b> True 300 690 120 20 True False 0 <b>Disk Usage</b> True 300 1000 700 280 True True automatic bottom-right True False GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK queue none 10 1030 100 30 True False 810 50 20 True False 0 0 <b>Time:</b> True 10 minutes 100 20 True True True False 70 2 hours 100 20 True True True False 180 20 True False 0 450 30 20 True False 450 380 True False 0 450 690 20 True False 0 450 1000 1 week 100 20 True True True False 290 1 year 100 20 True True True False 400 True False <b>Performance Graphs</b> True 15 True False Performance 1 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False 20 True False 0 AD is not currently configured for ''. To enable AD authentication, click Join. 20 20 Leave Domain 120 25 True False True True False 20 50 Join Domain 110 25 True False True True False 150 50 260 20 True False 0 Users and Groups with Access 20 90 40 True False 0 0 All changes made to users and groups on the access list will take effect at next login. 20 120 500 220 True True listusers False 0 Subject True 3 Roles True 1 Logged In 2 20 170 Add... 100 20 True False True True False 20 400 Remove 100 20 True False True True False 130 400 Logout User 100 20 True False True True False 240 400 Change Role... 100 20 True False True True False 350 400 True False <b>Active Directory Users</b> True 16 True False Users 3 False True False 0 none True True automatic automatic True False queue none True False True False Take Snapshot 100 20 True True True False 660 20 800 350 True True automatic automatic True False queue True True listvmsnapshots False False 0 column 1 20 45 New VM... 80 20 True False True True False 20 20 Create Template... 130 20 True False True True False 105 20 Export As Template... 150 20 True False True True False 240 20 Delete 70 20 True False True True False 585 20 Revert To... 80 20 True False True True False 500 20 Export As VM... 100 20 True True True False 395 20 True False <b>Virtual Machines Snapshots</b> True 17 True False Snapshots 14 False True False 0 none True False 12 True False True True automatic automatic True False queue True True 0 190 True False 100 20 True False 0 <b>Options:</b> True 10 10 Show unused network 170 20 True True False False True True 10 40 Show unused storage 170 20 True True False False True True 10 60 Show halted VMs 170 20 True True False False True True 10 80 Show network 170 20 True True False False True True 10 110 Show storage 170 20 True True False False True True 10 130 Export to PNG 100 20 True True True False 10 170 False True 1 True False <b>Maps</b> True 18 False Maps 20 False True False 0 none True False 12 True True automatic automatic True False queue none True False True False True False 3 2 True 12 12 True False <b>Event log</b> True 19 True False Logs 19 False True False 0 none True False 12 True False True False 95 35 True False Welcome to 13 14 150 22 True False OpenXenManager 110 20 600 40 True False 0 Click this button to connect to your XenServer and populate your server or Resource Pool with virtual machines 150 110 True True True True False none False 0 0 True False 0 0 images/server.jpg 9 52 320 20 True False 0 0 Need further advice, assistance or support? Visit: 14 280 https://sourceforge.net/projects/openxenmanager/forums/ 20 True True True False none False 0 https://sourceforge.net/projects/openxenmanager/forums/ 350 275 335 20 True False 0 You can download XenWebManager from here: 15 315 https://sourceforge.net/projects/xenwebmanager/ 20 True True True True False none https://sourceforge.net/projects/xenwebmanager/ 350 315 True False True 20 True False 0 0 Home True 20 False True False True True True True True False 2 False False True True True True 880 600 False 5 New VM center normal True False 2 True False end < Previous True False True True False False False 0 Next > True True True False False False 1 Finish True False True True False False False 2 Cancel True True True False False False 3 False True end 0 True False True False 1 True False 0 none 40 True False 600 25 True False 0 Select an operating system for the new virtual machine 73 7 32 32 True False images/newvm_32.png 13 7 True False True True True 0 450 True False 5 250 True False 0 etched-out True False 12 160 True False 200 25 True False 0.10000000149011612 <span background="blue" foreground="white"><b>Template </b></span> True 15 200 20 True False 0 Name 45 200 20 True False 0 Location 75 200 20 True False 0 Home Server 105 200 20 True False 0 CPU / Memory 135 200 20 True False 0 Virtual disks 165 200 20 True False 0 Virtual Interfaces 195 200 20 True False 0 Finish 225 True False 0 0 True False True 0 True False True False False True False True False 100 20 True False 0.20000000298023224 Templates: 20 15 550 250 True True automatic automatic True False queue 400 True True False False 1 0 350 Name 1 Category 3 20 40 550 120 True False 0 none True False 12 True True automatic automatic True False queue none True False True False label True True False Template details 22 315 60 20 True False 0 Search: 260 15 250 20 True True â— False False True True 320 15 True False Template False True False True False 50 20 True False 0 Name: 20 50 75 20 True False 0 Description: 20 85 400 20 True True 104 â— True True False False True True 120 50 420 100 True True automatic automatic True False queue True True 120 85 1 True False Name 1 False True False True False 400 20 True False 0 Provide a repository URL, physical drive or ISO image from which to install. 20 20 Install URL: 120 20 True True False False True True 34 60 Physical DVD Drive: 160 20 True True False False True True radiobutton1 34 100 ISO Image: 100 20 True True False False True True radiobutton1 34 140 400 20 True True â— False False True True 201 60 600 200 True False 100 20 True False 0 Example: 10 15 200 50 True False 0 0 http://server/path ftp://server/path nfs://server/path 25 34 400 20 True False 0 Advanced OS Boot Parameters: 10 102 550 20 True True â— False False True True 12 127 550 40 True False 0 0 Enter any additional advanced boot parameters here. If you do not wish to supply any advanced parameters leave this box blank 14 160 30 213 400 20 True False False 200 100 400 20 True False 200 140 2 True False Location 2 False True False True False 500 60 True False 0 0 The home server provides resources for a VM in a pool. OpenXenManager will always attempt to use the resources of the home server if it can. True end 40 34 500 140 True True automatic automatic True False queue True True listnewvmhosts False False 1 column 0 1 False 2 False 3 40 101 3 True False Home Server 3 False True False True False 150 20 True False 0 Number of vCPUs: 35 60 150 20 True False 0 Initial Memory: 35 95 100 20 True True 5 â— 1 False False True True initialmemory True 215 95 200 120 True False 0 in True False 12 140 110 True False 100 20 True False 0 Physical CPUs: 15 20 20 True False 0 0 110 15 100 20 True False 0 Total memory: 45 100 20 True False 0 Free memory: 75 50 20 True False 0 2000Mb 110 45 50 20 True False 0 1000Mb 110 75 True False <b>VM</b> True 43 148 100 20 True True 3 â— 1 False False True True numberofvcpus True 215 60 4 True False cpumemory 5 False True False True False 500 60 True False The default virtual disks for the template you have selected are listed below. You can add, modify or delete virtual disks, if required. When you have finished, click "Next" to continue to the next page. 45 40 300 20 True False 0 Virtual disks installed on the new machine: 45 113 Add... 70 25 True True True False 0 320 370 Edit... 70 25 True True True False 0 400 370 Delete 70 25 True True True False 0 480 370 520 210 True True automatic automatic True False queue True True listnewvmstorage False 0 Size (GB) 0 350 350 Location 350 1 Shared 2 45 155 5 True False virtual disks 5 False True False True False 500 60 True False The default virtual network interfaces for the template you have selected are listed below. You can add, modify or delete virtual network interface, if required. When you have finished, click "Next" to continue to the next page. 45 40 300 20 True False 0 Virtual network installed installed on the new machine: 45 113 Add 70 25 True True True False 0 400 370 Delete 70 25 True True True False 0 480 370 505 200 True True automatic automatic True False queue True True listnewvmnetworks False 0 Name 0 70 MAC Address 150 1 Network True False 0 2 45 155 6 True False virtual network 7 False True False True False 500 80 True False 0 0 You have successfully completed the new virtual machine wizard. To create the new virtual machine and close the wizard, click Finish. This process may take several minutes. 20 60 Start VM automatically 250 20 True True False False True 20 156 7 True False finish 7 False True True 1 True True 1 True True 1 previouswindownewvm nextwindownewvm finishwindownewvm cancelwindownewvm 750 False System Alerts center notification True True False 750 500 True False 700 400 True True automatic automatic True False queue True True listalerts False True 1 1 True both True 20 lbltreealerts4 0 Applies To lbltreealerts1 1 400 Details lbltreealerts2 2 Date lbltreealerts3 True 3 20 60 250 20 True False Current system alerts are listed below 69 20 40 40 True False gtk-dialog-warning 20 10 140 20 True False System alerts: 0 580 40 Dismiss selected 120 20 True True True False 20 465 Dismiss all 100 20 True True True False 145 465 Close 100 20 True True True False 620 465 450 False Copy Virtual Machine center 350 400 True False 45 20 True False 0 Name: 20 25 320 20 True True â— st False False True True 110 25 80 20 True False 0 Description: 20 55 320 20 True True â— False False True True 110 55 410 250 True False 0 in True False 12 True False Fast clone Clone the existing VM, using a storage-level fast disk clone operation. 400 60 True True False False True True 11 Full copy Create a full copy of the existing VM on this storage repository: 400 40 True True False False True True radiofastclone 63 380 100 True True automatic automatic True False queue True False True listcopystg False False column 0 2 #98da9ad091ff 3 124 True False Copy mode True 20 95 Delete original VM after copy 210 20 True True False False True 20 370 Copy 75 20 True True True False 265 370 Cancel 75 20 True True True False 350 370 False True False True False 25 True False Send Ctrl-Alt-Del 20 True True True False 0 5 Exit fullscreen 20 True True True False 0 150 5 False True 0 True True automatic automatic True False queue True False True True 1 800 600 False Console True False True False 25 True False Send Ctrl-Alt-Del 20 True True True False 0 5 Redock 20 True True True False 0 150 5 False True 0 True True automatic automatic True False queue True False True True 1 False popup Connecting.... True center splashscreen True True False 350 100 True False 300 30 True False 25 60 300 40 True False 0 0 Synchronizing 25 5 False popup OpenXenManager center dialogvmprop True False 300 100 True False 250 20 True False Preparing for import VM 25 39 250 20 True False 25 75 openxenmanager/oxcSERVER_newvm.py0000644000175000017500000002275611636446664015625 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERnewvm: def get_path_available_host(self): path = 0 i = 0 for host in self.all_hosts.keys(): if self.all_hosts[host]['enabled']: path = i i = i + 1 return path def first_network(self): for network in self.all_network: return self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network ') def first_network_ref(self): for network in self.all_network: return network def fill_listnewvmstorage(self, list, vm, host, ref): list.clear() if "disks" in self.all_vms[vm]['other_config']: dom = xml.dom.minidom.parseString(self.all_vms[vm]['other_config']['disks']) nodes = dom.getElementsByTagName("disk") for node in nodes: if self.default_sr == "OpaqueRef:NULL" or self.default_sr not in self.all_storage: self.default_sr = self.all_storage.keys()[0] list.append(["%0.2f" % (float(node.attributes.getNamedItem("size").value)/1024/1024/1024), self.all_storage[self.default_sr]['name_label'] + " on " + self.all_hosts[host]['name_label'], str(self.all_storage[self.default_sr]['shared']),ref]) else: for vbd in self.all_vbd: if self.all_vbd[vbd]['VM'] == vm: if self.all_vbd[vbd]["type"] == "Disk": vdi = self.all_vbd[vbd]["VDI"] list.append(["%0.2f" % (float(self.all_vdi[vdi]["virtual_size"])/1024/1024/1024), self.all_storage[self.default_sr]['name_label'] + " on " + self.all_hosts[host]['name_label'], str(self.all_storage[self.default_sr]['shared']),ref]) def fill_listnewvmdisk(self, list, host): list.clear() i = 0 default_sr = 0 for sr in self.all_storage.keys(): storage = self.all_storage[sr] if storage['type'] != "iso" and storage['type'] != "udev": if self.default_sr == sr: default_sr = i try: list.append([storage['name_label'], storage['name_description'], self.convert_bytes(storage['physical_size']), self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])), sr]) except: pass i = i + 1 return default_sr def create_newvm(self, data): res = self.connection.VM.clone(self.session_uuid, data['ref'], data['name']) if not "Value" in res: self.wine.show_error_dlg(str(res["ErrorDescription"])) return vm_uuid = res['Value'] if data["startvm"]: self.autostart[vm_uuid] = data['host'] self.connection.VM.set_name_description(self.session_uuid, vm_uuid, data['description']) other_config = self.all_vms[data['ref']]['other_config'] other_config["default_template"] = "false" selection = self.wine.builder.get_object("treenewvmstorage").get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) selection.select_all() model, selected = selection.get_selected_rows() iters = [model.get_iter(path) for path in selected] i = 0 disk = "" for iter_ref in iters: size = int(float(self.wine.builder.get_object("listnewvmstorage").get_value(iter_ref, 0))*1024*1024*1024) sr = self.all_storage[self.wine.builder.get_object("listnewvmstorage").get_value(iter_ref, 3)]["uuid"] if "postinstall" not in other_config and data["location"] != "radiobutton1": disk += '' % (i, size, sr) else: if i == 0: disk += '' % (i, size, sr) else: disk += '' % (i, size, sr) i = i + 1 disk += "" setdisks = True for vbd in self.all_vbd: if self.all_vbd[vbd]['VM'] == data["ref"]: if self.all_vbd[vbd]["type"] == "Disk": setdisks = False if setdisks: other_config['disks'] = disk selection.unselect_all() self.connection.VM.set_affinity(self.session_uuid, data['host']) if "postinstall" not in other_config: if data["location"] == "radiobutton1": other_config["install-repository"] = data['location_url'] else: other_config["install-repository"] = "cdrom" from uuid import uuid1 as uuid other_config["mac_seed"] = str(uuid()) self.connection.VM.set_other_config(self.session_uuid, vm_uuid, other_config) ref = self.connection.VM.provision( self.session_uuid, vm_uuid) self.track_tasks[ref['Value']] = vm_uuid vif_cfg = { 'uuid': '', 'allowed_operations': [], 'current_operations': {}, 'device': '0', 'network': '', 'VM': '', 'MAC': '', 'MTU': '0', "other_config": {}, 'currently_attached': False, 'status_code': "0", 'status_detail': "", "runtime_properties": {}, "qos_algorithm_type": "", "qos_algorithm_params": {}, "metrics": "", 'MAC_autogenerated': False } vbd_cfg = { 'VM': vm_uuid, 'VDI': data['vdi'], 'userdevice': str(len(iters)+1), 'bootable': True, 'mode': "RO", 'type': "CD", 'unplugabble': "0", 'storage_lock': "0", 'empty': False, 'currently_attached': "0", 'status_code': "0", 'other_config': {}, 'qos_algorithm_type': "", 'qos_algorithm_params': {}, } if data['vdi']: res = self.connection.VBD.create(self.session_uuid, vbd_cfg) self.connection.VBD.insert(self.session_uuid, res['Value'], data['vdi']) selection = self.wine.builder.get_object("treenewvmnetwork").get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) selection.select_all() model, selected = selection.get_selected_rows() iters = [model.get_iter(path) for path in selected] i = 0 memory = int(data['memorymb']) res = self.connection.VM.set_memory_limits(self.session_uuid, ref, str(16777216), str(int(memory*1024*1024)), str(int(memory*1024*1024)), str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: if res["ErrorDescription"][0] == "MESSAGE_METHOD_UNKNOWN": self.connection.VM.set_memory_static_min(self.session_uuid, vm_uuid, str(memory*1024*1024)) self.connection.VM.set_memory_dynamic_min(self.session_uuid, vm_uuid, str(memory*1024*1024)) self.connection.VM.set_memory_static_max(self.session_uuid, vm_uuid, str(memory*1024*1024)) self.connection.VM.set_memory_dynamic_max(self.session_uuid, vm_uuid, str(memory*1024*1024)) self.connection.VM.set_VCPUs_max (self.session_uuid, vm_uuid, str(int(data['numberofvcpus']))) self.connection.VM.set_VCPUs_at_startup(self.session_uuid, vm_uuid, str(int(data['numberofvcpus']))) self.connection.VM.set_PV_args(self.session_uuid, vm_uuid, data['entrybootparameters']) for iter_ref in iters: vif_cfg['device'] = str(i) vif_cfg['network'] = self.wine.builder.get_object("listnewvmnetworks").get_value(iter_ref, 3) vif_cfg['VM'] = vm_uuid self.connection.VIF.create(self.session_uuid, vif_cfg) i = i +1 openxenmanager/window_storage.py0000644000175000017500000011574311636446664015723 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import re import gtk class oxcWindowStorage: """ Class to manage "storage" elements """ reattach_lun = None def on_rescanisos_clicked(self, widget, data=None): self.xc_servers[self.selected_host].rescan_isos(self.selected_ref) def on_treerepairstorage_cursor_changed(self, widget, data=None): widget.get_selection().unselect_all() def on_acceptnewstgreattachnfs_clicked(self, widget, data=None): """ Function called when you press accept on "reattach window storage" (nfs) """ name = self.builder.get_object("txtnewstgnfsname").get_text() host, path = self.builder.get_object("txtnewstgnfspath").get_text().split(":", 2) options = self.builder.get_object("txtnewstgnfsoptions").get_text() create = self.builder.get_object("radiocreatenewsr").get_active() ref = None treereattachnewstgnfs = self.builder.get_object("treereattachnewstgnfs") listreattachnewstgnfs = self.builder.get_object("listreattachnewstgnfs") selection = treereattachnewstgnfs.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] uuid = listreattachnewstgnfs.get_value(iter, 0) self.xc_servers[self.selected_host].reattach_nfs_vhd(self.selected_ref, name, host, path, options, create, uuid) # Hide confirmation window self.builder.get_object("newstgreattachnfs").hide() # Hide new storage window self.builder.get_object("newstorage").hide() def on_acceptnewstgreattachaoe_clicked(self, widget, data=None): """ Function called when you press accept on "reattach window storage" (aoe) """ name = self.builder.get_object("txtnewstgaoename").get_text() path = self.builder.get_object("txtnewstgaoepath").get_text() create = self.builder.get_object("radiocreatenewaoe").get_active() ref = None treereattachnewstgnfs = self.builder.get_object("treereattachnewstgaoe") listreattachnewstgnfs = self.builder.get_object("listreattachnewstgaoe") selection = treereattachnewstgnfs.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] uuid = listreattachnewstgnfs.get_value(iter, 0) self.xc_servers[self.selected_host].reattach_aoe(self.selected_ref, name, path, create, uuid) # Hide confirmation window self.builder.get_object("newstgreattachaoe").hide() # Hide new storage window self.builder.get_object("newstorage").hide() def on_treehbalun_cursor_changed(self, widget, data=None): listhbalun = self.builder.get_object("listhbalun") selection = widget.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] if listhbalun.get_value(iter, 1): uuid = listhbalun.get_value(iter, 2) self.builder.get_object("finishnewstorage").set_sensitive(True) else: self.builder.get_object("finishnewstorage").set_sensitive(False) else: self.builder.get_object("finishnewstorage").set_sensitive(False) def on_cancelnewstgreattachnfs_clicked(self, widget, data=None): """ Function called when you press cancel on "reattach window storage" (nfs) """ # Hide confirmation window self.builder.get_object("newstgreattachnfs").hide() def on_cancelnewstgreattachaoe_clicked(self, widget, data=None): """ Function called when you press cancel on "reattach window storage" (aoe) """ # Hide confirmation window self.builder.get_object("newstgreattachaoe").hide() def on_finishnewstorage_clicked(self, widget, data=None): """ Function called when you press "Finish" on new storage wizard """ page = self.builder.get_object("tabboxnewstorage").get_current_page() if page == 1: # NFS VHD name = self.builder.get_object("txtnewstgnfsname").get_text() host, path = self.builder.get_object("txtnewstgnfspath").get_text().split(":", 2) options = self.builder.get_object("txtnewstgnfsoptions").get_text() create = self.builder.get_object("radiocreatenewsr").get_active() if not create: labels = self.builder.get_object("newstgreattachnfs").get_children()[0].get_children()[0].get_children()[1].get_children() treereattachnewstgnfs = self.builder.get_object("treereattachnewstgnfs") listreattachnewstgnfs = self.builder.get_object("listreattachnewstgnfs") selection = treereattachnewstgnfs.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] uuid = listreattachnewstgnfs.get_value(iter, 0) labels[0].set_text(labels[0].get_text().replace("{0}", uuid)) self.builder.get_object("newstgreattachnfs").show() else: self.xc_servers[self.selected_host].create_nfs_vhd(self.selected_ref, name, host, path, options, create) self.builder.get_object("newstorage").hide() elif page == 2: # iSCSI name = self.builder.get_object("txtiscsiname").get_text() host = self.builder.get_object("txtiscsitarget").get_text() port = self.builder.get_object("txtiscsiport").get_text() chapuser = None chapsecret = None if self.builder.get_object("checkscsichap").get_active(): chapuser = self.builder.get_object("txtiscsichapuser").get_text() chapsecret = self.builder.get_object("txtiscsichapsecret").get_text() combotargetiqn = self.builder.get_object("combotargetiqn") listtargetiqn = self.builder.get_object("listtargetiqn") combotargetlun = self.builder.get_object("combotargetlun") listtargetlun = self.builder.get_object("listtargetlun") targetiqn = listtargetiqn.get_value(combotargetiqn.get_active_iter(), 0) targetlun = listtargetlun.get_value(combotargetlun.get_active_iter(), 0) self.reattach_lun = self.xc_servers[self.selected_host].check_iscsi(self.selected_ref, name, host, port, targetlun, targetiqn, chapuser, chapsecret) if self.reattach_lun: # If create_iscsi return an uuid.. then ask confirmation for reattach, format or cancel self.builder.get_object("reattachformatiscsidisk").show() else: # If not, only ask for format or cancel self.builder.get_object("formatiscsidisk").show() elif page == 3: # Hardware HBA treehbalun = self.builder.get_object("treehbalun") listhbalun = self.builder.get_object("listhbalun") selection = treehbalun.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] text = listhbalun.get_value(iter, 0) uuid = listhbalun.get_value(iter, 2) path = listhbalun.get_value(iter, 3) option = self.xc_servers[self.selected_host].check_hardware_hba(self.selected_ref, uuid, text) if option[0] == 0: # Ok, do you want format it? self.builder.get_object("formatdisklun").show() elif option[0] == 1: # Sorry, detach first self.builder.get_object("detachhbalun").show() label = self.builder.get_object("detachhbalun").get_children()[0].get_children()[0].get_children()[1].get_children()[0] label.set_text(label.get_text().replace("{0}", option[1]).replace("{1}", option[2])) elif option[0] == 2: # Do you want reattach? self.builder.get_object("reattachhbalun").show() label = self.builder.get_object("reattachhbalun").get_children()[0].get_children()[0].get_children()[1].get_children()[0] label.set_text(label.get_text().replace("{0}", option[1])) elif option[0] == 3: # Do you want reattach or format? self.builder.get_object("reattachformathbalun").show() label = self.builder.get_object("reattachformathbalun").get_children()[0].get_children()[0].get_children()[1].get_children()[0] label.set_text(label.get_text().replace("{0}", option[1])) elif page == 4: # CIFS ISO name = self.builder.get_object("txtnewstgcifsname").get_text() sharename = self.builder.get_object("txtnewstgcifspath").get_text() options = self.builder.get_object("txtnewstgcifsoptions").get_text() if self.builder.get_object("checknewstgcifslogin").get_active(): username = self.builder.get_object("txtnewstgcifsusername").get_text() password = self.builder.get_object("txtnewstgcifspassword").get_text() else: username = "" password = "" # Check if is a reattach or a new attach if self.reattach_storage: # reattach_nfs_iso returns 0 if iso library was attached correctly if self.xc_servers[self.selected_host].reattach_cifs_iso(self.selected_ref, name, \ sharename, options, username,password) == 0: # hide the window self.builder.get_object("newstorage").hide() else: # create_cifs_iso returns 0 if iso library was attached correctly if self.xc_servers[self.selected_host].create_cifs_iso(self.selected_ref, name, \ sharename, options, username,password) == 0: # hide the window self.builder.get_object("newstorage").hide() pass elif page == 5: # NFS ISO name = self.builder.get_object("txtnewstgnfsisoname").get_text() sharename = self.builder.get_object("txtnewstgnfsisopath").get_text() options = self.builder.get_object("txtnewstgnfsisooptions").get_text() # Check if is a reattach or a new attach if self.reattach_storage: # reattach_nfs_iso returns 0 if iso library was attached correctly if self.xc_servers[self.selected_host].reattach_nfs_iso(self.selected_ref, name, sharename, options) == 0: # hide the window self.builder.get_object("newstorage").hide() else: # create_nfs_iso returns 0 if iso library was attached correctly if self.xc_servers[self.selected_host].create_nfs_iso(self.selected_ref, name, sharename, options) == 0: # hide the window self.builder.get_object("newstorage").hide() if page == 6: # Experimental AOE name = self.builder.get_object("txtnewstgaoename").get_text() path = self.builder.get_object("txtnewstgaoepath").get_text() create = self.builder.get_object("radiocreatenewaoe").get_active() if not create: labels = self.builder.get_object("newstgreattachaoe").get_children()[0].get_children()[0].get_children()[1].get_children() treereattachnewstgnfs = self.builder.get_object("treereattachnewstgaoe") listreattachnewstgnfs = self.builder.get_object("listreattachnewstgaoe") selection = treereattachnewstgnfs.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] uuid = listreattachnewstgnfs.get_value(iter, 0) labels[0].set_text(labels[0].get_text().replace("{0}", uuid)) self.builder.get_object("newstgreattachaoe").show() else: self.xc_servers[self.selected_host].create_aoe(self.selected_ref, name, path, create) self.builder.get_object("newstorage").hide() def on_cancelformatdisklun_clicked(self, widget, data=None): """ Function called when you cancel format disk confirmation (hba) """ self.builder.get_object("formatdisklun").hide() def on_accepformatdisklun_clicked(self, widget, data=None): """ Function called when you accept format disk confirmation (hba) and function called when you choose Format button on reattach/format disk confirmation (hba) """ treehbalun = self.builder.get_object("treehbalun") listhbalun = self.builder.get_object("listhbalun") selection = treehbalun.get_selection() if selection.get_selected()[1]: iter = selection.get_selected()[1] text = listhbalun.get_value(iter, 0) uuid = listhbalun.get_value(iter, 2) path = listhbalun.get_value(iter, 3) name = self.builder.get_object("txthbaname").get_text() option = self.xc_servers[self.selected_host].format_hardware_hba(self.selected_ref, uuid, name, path) self.builder.get_object("formatdisklun").hide() self.builder.get_object("newstorage").hide() def on_acceptdetachhbalun_clicked(self, widget, data=None): """ Function called when you accept information dialog informing you must detach the SR """ self.builder.get_object("detachhbalun").hide() def on_cancelreattachhbalun_clicked(self, widget, data=None): """ Function called when you cancel reattach disk confirmation (hba) """ self.builder.get_object("reattachhbalun").hide() def on_acceptreattachhbalun_clicked(self, widget, data=None): """ Function called when you accept reattach disk confirmation (hba) """ treehbalun = self.builder.get_object("treehbalun") listhbalun = self.builder.get_object("listhbalun") selection = treehbalun.get_selection() option = 1 if selection.get_selected()[1]: iter = selection.get_selected()[1] text = listhbalun.get_value(iter, 0) uuid = listhbalun.get_value(iter, 2) path = listhbalun.get_value(iter, 3) name = self.builder.get_object("txthbaname").get_text() option = self.xc_servers[self.selected_host].reattach_hardware_hba(self.selected_ref, uuid, name, path) self.builder.get_object("reattachhbalun").hide() if option == 0: self.builder.get_object("newstorage").hide() def on_acceptareattachformathbalun_clicked(self, widget, data=None): """ Function called when you press reattach button reattach/format disk confirmation (hba) """ treehbalun = self.builder.get_object("treehbalun") listhbalun = self.builder.get_object("listhbalun") selection = treehbalun.get_selection() option = 1 if selection.get_selected()[1]: iter = selection.get_selected()[1] text = listhbalun.get_value(iter, 0) uuid = listhbalun.get_value(iter, 2) path = listhbalun.get_value(iter, 3) name = self.builder.get_object("txthbaname").get_text() option = self.xc_servers[self.selected_host].reattach_and_introduce_hardware_hba(self.selected_ref, uuid, name, path) self.builder.get_object("reattachformathbalun").hide() if option == 0: self.builder.get_object("newstorage").hide() def on_cancelreattachformathbalun_clicked(self, widget, data=None): """ Function called when you cancel reattach/format disk confirmation (hba) """ self.builder.get_object("reattachformathbalun").hide() def on_acceptformatiscsidisk_clicked(self, widget, data=None): """ Function called when you accept "format" scsi lun confirmation dialog """ name = self.builder.get_object("txtiscsiname").get_text() host = self.builder.get_object("txtiscsitarget").get_text() port = self.builder.get_object("txtiscsiport").get_text() chapuser = None chapsecret = None if self.builder.get_object("checkscsichap").get_active(): chapuser = self.builder.get_object("txtiscsichapuser").get_text() chapsecret = self.builder.get_object("txtiscsichapsecret").get_text() combotargetiqn = self.builder.get_object("combotargetiqn") listtargetiqn = self.builder.get_object("listtargetiqn") combotargetlun = self.builder.get_object("combotargetlun") listtargetlun = self.builder.get_object("listtargetlun") targetiqn = listtargetiqn.get_value(combotargetiqn.get_active_iter(), 0) targetlun = listtargetlun.get_value(combotargetlun.get_active_iter(), 0) # Create formating the SCSI lun self.xc_servers[self.selected_host].create_iscsi(self.selected_ref, name, host, port, targetlun, targetiqn, chapuser, chapsecret) # Hide the dialog self.builder.get_object("formatiscsidisk").hide() self.builder.get_object("reattachformatiscsidisk").hide() # hide the window self.builder.get_object("newstorage").hide() def on_reattachscsidisk_clicked(self, widget, data=None): """ Function called when you choose "reattach" scsi lun confirmation dialog """ name = self.builder.get_object("txtiscsiname").get_text() host = self.builder.get_object("txtiscsitarget").get_text() port = self.builder.get_object("txtiscsiport").get_text() chapuser = None chapsecret = None if self.builder.get_object("checkscsichap").get_active(): chapuser = self.builder.get_object("txtiscsichapuser").get_text() chapsecret = self.builder.get_object("txtiscsichapsecret").get_text() combotargetiqn = self.builder.get_object("combotargetiqn") listtargetiqn = self.builder.get_object("listtargetiqn") combotargetlun = self.builder.get_object("combotargetlun") listtargetlun = self.builder.get_object("listtargetlun") targetiqn = listtargetiqn.get_value(combotargetiqn.get_active_iter(), 0) targetlun = listtargetlun.get_value(combotargetlun.get_active_iter(), 0) # Reattach the SCSI lun self.xc_servers[self.selected_host].reattach_iscsi(self.selected_ref, name, host, port, targetlun, targetiqn, chapuser, chapsecret, self.reattach_lun) # Hide the dialog self.builder.get_object("reattachformatiscsidisk").hide() # hide the window self.builder.get_object("newstorage").hide() def on_cancelformatiscsidisk_clicked(self, widget, data=None): """ Function called when you cancel "format" scsi lun confirmation dialog """ # Hide the dialog self.builder.get_object("formatiscsidisk").hide() def on_cancelreattachscsidisk_clicked(self, widget, data=None): """ Function called when you cancel "reattach format" scsi lun confirmation dialog """ # Hide the dialog self.builder.get_object("reattachformatiscsidisk").hide() def on_nextnewstorage_clicked(self, widget, data=None): """ Function called when you press "Next" on new storage wizard """ mapping = { "radionewstgnfsvhd" : 1, "radionewstgiscsi" : 2, "radionewstghwhba" : 3, "radionewstgcifs" : 4, "radionewstgnfsiso": 5, "radionewstgaoe": 6 } for radio in mapping: if self.builder.get_object(radio).get_active(): # Set the correct tab for selected storage type self.builder.get_object("tabboxnewstorage").set_current_page(mapping[radio]) # Empty previous text/set default texts self.builder.get_object("txtnewstgnfsname").set_text("NFS virtual disk storage") self.builder.get_object("txtnewstgnfspath").set_text("") self.builder.get_object("txtnewstgnfsoptions").set_text("") self.builder.get_object("listreattachnewstgnfs").clear() self.builder.get_object("txtiscsiname").set_text("iSCSI virtual disk storage") self.builder.get_object("txtiscsitarget").set_text("") self.builder.get_object("txtiscsiport").set_text("3260") self.builder.get_object("txtiscsichapuser").set_text("") self.builder.get_object("txtiscsichapsecret").set_text("") self.builder.get_object("listtargetiqn").clear() self.builder.get_object("listtargetlun").clear() self.builder.get_object("txtnewstgcifsname").set_text("CIFS ISO library") self.builder.get_object("txtnewstgcifspath").set_text("") self.builder.get_object("txtnewstgcifsusername").set_text("") self.builder.get_object("txtnewstgcifspassword").set_text("") self.builder.get_object("txtnewstgcifsoptions").set_text("") self.builder.get_object("txtnewstgnfsisoname").set_text("NFS ISO library") self.builder.get_object("txtnewstgnfsisopath").set_text("") self.builder.get_object("txtnewstgnfsisooptions").set_text("") self.builder.get_object("txtnewstgnfspath").grab_focus() self.builder.get_object("txtiscsitarget").grab_focus() # Disable Next button widget.set_sensitive(False) # Enable Previous button self.builder.get_object("previousnewstorage").set_sensitive(True) # if Hardware HBA is selected.. if self.builder.get_object("radionewstghwhba").get_active(): listhbalun = self.builder.get_object("listhbalun") if self.xc_servers[self.selected_host].fill_hw_hba(self.selected_ref, listhbalun) == 1: # no LUNs found self.builder.get_object("tabboxnewstorage").set_current_page(0) def on_cancelnewstorage_clicked(self, widget, data=None): """ Function called when you press "Cancel" on new storage wizard """ self.builder.get_object("newstorage").hide() def on_previousnewstorage_clicked(self, widget, data=None): """ Function called when you press "Previous" on new storage wizard """ self.builder.get_object("tabboxnewstorage").set_current_page(0) # Disable Previous button widget.set_sensitive(False) # Enable Next button self.builder.get_object("nextnewstorage").set_sensitive(True) def on_radioreattachsr_toggled(self, widget, data=None): """ Function called when you choose "Reattach an existing SR" radio on new storage (nfs) """ # Enable tree with possible attach storage if radio is selected self.builder.get_object("treereattachnewstgnfs").set_sensitive(widget.get_active()) def on_btnewstgsnfsscan_clicked(self, widget, data=None): """ Function called when you press on "Scan" button on new storage (nfs) """ host, path = self.builder.get_object("txtnewstgnfspath").get_text().split(":", 2) options = self.builder.get_object("txtnewstgnfsoptions").get_text() listreattachnewstgnfs = self.builder.get_object("listreattachnewstgnfs") # Scan for NFS on selected host and path result = self.xc_servers[self.selected_host].scan_nfs_vhd(self.selected_ref, listreattachnewstgnfs, host, path, options) if result == 1: # Connection OK, but not exists previous SR self.builder.get_object("treereattachnewstgnfs").set_sensitive(False) self.builder.get_object("radioreattachsr").set_sensitive(False) self.builder.get_object("finishnewstorage").set_sensitive(True) elif result == 2: # Connection OK and exists previous SR self.builder.get_object("radioreattachsr").set_sensitive(True) self.builder.get_object("finishnewstorage").set_sensitive(True) self.builder.get_object("treereattachnewstgnfs").set_sensitive(True) # Select the first as default treereattachnewstgnfs = self.builder.get_object("treereattachnewstgnfs") treereattachnewstgnfs.set_cursor((0,), treereattachnewstgnfs.get_column(0)) treereattachnewstgnfs.get_selection().select_path((0, 0)) else: # Connection ERROR self.builder.get_object("treereattachnewstgnfs").set_sensitive(False) self.builder.get_object("radioreattachsr").set_sensitive(False) self.builder.get_object("finishnewstorage").set_sensitive(False) def on_btnewstgsaoescan_clicked(self, widget, data=None): """ Function called when you press on "Scan" button on new storage (aoe) """ path = self.builder.get_object("txtnewstgaoepath").get_text() listreattachnewstgaoe = self.builder.get_object("listreattachnewstgaoe") # Scan for AOE on selected device result = self.xc_servers[self.selected_host].scan_aoe(self.selected_ref, listreattachnewstgaoe, path) if result == 1: # Connection OK, but not exists previous SR self.builder.get_object("treereattachnewstgaoe").set_sensitive(False) self.builder.get_object("radioreattachaoe").set_sensitive(False) self.builder.get_object("finishnewstorage").set_sensitive(True) elif result == 2: # Connection OK and exists previous SR self.builder.get_object("treereattachnewstgaoe").set_sensitive(True) self.builder.get_object("radioreattachaoe").set_sensitive(True) self.builder.get_object("finishnewstorage").set_sensitive(True) # Select the first as default treereattachnewstgaoe = self.builder.get_object("treereattachnewstgaoe") treereattachnewstgaoe.set_cursor((0,), treereattachnewstgaoe.get_column(0)) treereattachnewstgaoe.get_selection().select_path((0, 0)) else: # Connection ERROR self.builder.get_object("treereattachnewstgaoe").set_sensitive(False) self.builder.get_object("radioreattachaoe").set_sensitive(False) self.builder.get_object("finishnewstorage").set_sensitive(False) def on_txtnewstgcifspath_changed(self, widget, data=None): """ Function called when you change text on "Share Name" on new storage (cifs iso) """ X = "\\\\(\S+)\\\\(\S+)" c = re.compile(X).search(widget.get_text()) self.builder.get_object("finishnewstorage").set_sensitive(c != None) def on_txtnewstgnfsisopath_changed(self, widget, data=None): """ Function called when you change text on "Share Name" on new storage (nfs iso) """ X = "(\S+):\/(\S+)" c = re.compile(X).search(widget.get_text()) self.builder.get_object("finishnewstorage").set_sensitive(c != None) def on_txtnewstgnfspath_changed(self, widget, data=None): """ Function called when you change text on "Share Name" on new storage (nfs) """ X = "(\S+):\/(\S+)" c = re.compile(X).search(widget.get_text()) self.builder.get_object("btnewstgsnfsscan").set_sensitive(c != None) def on_btdiscoveriqns_clicked(self, widget, data=None): """ Function called when you press on "Discover IQNs" button on new storage (scsi) """ target = self.builder.get_object("txtiscsitarget").get_text() iscsiport = self.builder.get_object("txtiscsiport").get_text() if self.builder.get_object("checkscsichap").get_active(): user = self.builder.get_object("txtiscsichapuser").get_text() password = self.builder.get_object("txtiscsichapsecret").get_text() else: user = None password = None combotargetiqn = self.builder.get_object("combotargetiqn") listtargetiqn = self.builder.get_object("listtargetiqn") # fill_iscsi_target_iqn fills the combo with possible iqn targets and return True if something was found if self.xc_servers[self.selected_host].fill_iscsi_target_iqn(self.selected_ref, listtargetiqn, \ target, iscsiport, user, password): # Set the first as default combotargetiqn.set_active(0) self.builder.get_object("btdiscoverluns").set_sensitive(True) else: self.builder.get_object("btdiscoverluns").set_sensitive(False) def on_btdiscoverluns_clicked(self, widget, data=None): """ Function called when you press on "Discover LUNs" button on new storage (scsi) """ target = self.builder.get_object("txtiscsitarget").get_text() iscsiport = self.builder.get_object("txtiscsiport").get_text() if self.builder.get_object("checkscsichap").get_active(): user = self.builder.get_object("txtiscsichapuser").get_text() password = self.builder.get_object("txtiscsichapsecret").get_text() else: user = None password = None combotargetiqn = self.builder.get_object("combotargetiqn") listtargetiqn = self.builder.get_object("listtargetiqn") combotargetlun = self.builder.get_object("combotargetlun") listtargetlun = self.builder.get_object("listtargetlun") targetiqn = listtargetiqn.get_value(combotargetiqn.get_active_iter(), 0) # fill_iscsi_target_lun fills the combo with possible luns and return True if something was found if self.xc_servers[self.selected_host].fill_iscsi_target_lun(self.selected_ref, listtargetlun, \ target, targetiqn, iscsiport, user, password): # Set the first as default # TODO: detect if uuid is in use combotargetlun.set_active(0) self.builder.get_object("finishnewstorage").set_sensitive(True) else: self.builder.get_object("finishnewstorage").set_sensitive(False) def on_checkscsichap_toggled(self, widget, data=None): """ Function called when you check "Use CHAP" on new storage (scsi) """ # Hide if is unchecked self.builder.get_object("framechap").set_sensitive(widget.get_active()) def on_txtiscsitarget_changed(self, widget, data=None): """ Function called when text changed on "target host" on "new storage" (iscsi) """ # Disable or enabled "Discover IQNs" button self.builder.get_object("btdiscoveriqns").set_sensitive(len(widget.get_text()) > 0) def on_btstgnewdisk_activate(self, widget, data=None): """" Function called when you press "new disk" on storage """ vmaddnewdisk = self.builder.get_object("vmaddnewdisk") # Set default name and empty description self.builder.get_object("vmaddnewdisk_name").set_text("New virtual disk on " + self.selected_name) self.builder.get_object("vmaddnewdisk_desc").set_text("") listnewvmdisk1 = self.builder.get_object("listnewvmdisk1") # Fill the possible disks list defsr = self.xc_servers[self.selected_host].fill_listnewvmdisk(listnewvmdisk1, self.selected_host) treenewvmstorage1 = self.builder.get_object("treenewvmdisk1") # Select the first by default treenewvmstorage1.set_cursor((defsr,), treenewvmstorage1.get_column(0)) treenewvmstorage1.get_selection().select_path((defsr, 0)) # Set as default "5 Gb" size self.builder.get_object("disksize2").set_value(float(5)) # Show the "add new disk" window vmaddnewdisk.show() def on_radionewstgnfsvhd_group_changed(self, widget, data=None): """ Function called when you select a type the storage on "new storage" window """ if widget.get_active(): texts = { "radionewstgnfsvhd" : "NFS servers are a common form of shared filesystem infrastructure, and can be used as a storage repository substrate for virtual disks.\n\nAs NFS storage repositories are shared, the virtual disks stored in them allow VMs to be started on any server in a resource pool and to be migrated between them using XenMotion.\n\nWhen you configure an NFS storage repository, you simply provide the hostname or IP address of the NFS server and the path to a directory that will be used to contain the storage repository. The NFS server must be configured to export the specified path to all servers in the pool", "radionewstgiscsi" : "Shared Logical Volume Manager (LVM) support is available using either iSCSI or Fibre Channel access to a shared LUN.\n\nUsing the LVM-based shared SR provides the same performance benefits as unshared LVM for local disk storage, however in the shared context, iSCSI or Fibre Channel-based SRs enable VM agility -- VMs may be started on any server in a pool and migrated between them.", "radionewstghwhba" : "XenServer Hosts support Fibre Channel (FC) storage area networks (SANs) through Emulex or QLogic host bus adapters (HBAs).\n\nAll FC configuration required to expose a FC LUN to the host must be completed manually, including storage devices, network devices, and the HBA within the XenServer host.\n\nOnce all FC configuration is complete the HBA will expose a SCSI device backed by the FC LUN to the host. The SCSI device can then be used to access to the FC LUN as if it were a locally attached SCSI device.", "radionewstgnetapp" : "Main developer of openxenmanager hasn't NetApp and hasn't Essentials", "radionewstgdell" : "Main developer of openxenmanager hasn't Dell EqualLogic and hasn't Essentials", "radionewstgcifs" : "Select this option if you have a library of VM installation ISO images available as a Windows File Sharing share that you wish to attach to your host or pool.", "radionewstgnfsiso": "Select this option if you have a library of VM installation ISO images available as a NFS share that you wish to attach to your host or pool.", "radionewstgaoe": "ATA over Ethernet (AoE) is a network protocol designed for simple, high-performance access of SATA storage devices over Ethernet networks. It gives the possibility to build SANs with low-cost, standard technologies." } name = gtk.Buildable.get_name(widget) # Set the info text self.builder.get_object("newstorageinfo").set_text(texts[name]) if name != "radionewstgnetapp" and name != "radionewstgdell": # Enable buton for others self.builder.get_object("nextnewstorage").set_sensitive(True) else: # Disable next button on unsupported new storage types self.builder.get_object("nextnewstorage").set_sensitive(False) def on_btstgremove_activate(self, widget, data=None): """" Function called when you press "remove disk" on storage """ is_a_snapshot = self.xc_servers[self.selected_host].all_vdi[self.selected_vdi_ref]['is_a_snapshot'] if is_a_snapshot: # If is a snapshot, show different text self.builder.get_object("dialogdeletevdi").set_title("Delete entire snapshot") self.builder.get_object("dialogdeletevdi").set_markup("Deleting a single snapshot disk is not allowed. This action will delete the entire snapshot, and any other disks attache. This operation cannot be undone. Dou wish continue?") else: # else show the confirmation text self.builder.get_object("dialogdeletevdi").set_title("Delete Virtual Disk") self.builder.get_object("dialogdeletevdi").set_markup("This will delete this virtual disk permanently destroying the data on it. Continue?") # Show the confirmation dialog self.builder.get_object("dialogdeletevdi").show() def on_treestg_button_press_event(self, widget, event): """" Function called when you select a storage on "tree storage" tree """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo widget.grab_focus() widget.set_cursor( path, col, 0) iter = self.builder.get_object("liststg").get_iter(path) self.selected_vdi_ref = self.builder.get_object("liststg").get_value(iter, 0) operations = self.xc_servers[self.selected_host].all_vdi[self.selected_vdi_ref]['allowed_operations'] # If have "destroy" option in "allowed_operations" if operations.count("destroy"): # Enable button self.builder.get_object("btstgremove").set_sensitive(True) else: # Else disable it self.builder.get_object("btstgproperties").set_sensitive(True) is_snapshot = self.xc_servers[self.selected_host].all_vdi[self.selected_vdi_ref]['is_a_snapshot'] # If not is a snapshot, enable "properties" button self.builder.get_object("btstgproperties").set_sensitive(not is_snapshot) def on_dialogdeletevdi_cancel_activate(self, widget, data=None): """ Function called when you cancel "dialog delete" """ self.builder.get_object("dialogdeletevdi").hide() def on_dialogdeletevdi_accept_activate(self, widget, data=None): """ Function called when you accept "dialog delete" """ vdi = self.xc_servers[self.selected_host].all_vdi[self.selected_vdi_ref] if vdi['is_a_snapshot']: # If is a snapshot, destroy entire snapshot for vbd_ref in vdi['VBDs']: ref = self.xc_servers[self.selected_host].all_vbd[vbd_ref]["VM"] self.xc_servers[self.selected_host].destroy_vm(ref, True, False) else: if len(vdi['VBDs']): vm_ref = self.xc_servers[self.selected_host].all_vbd[vdi['VBDs'][0]]['VM'] else: vm_ref = None # Else only delete select virtual disk self.xc_servers[self.selected_host].delete_vdi(self.selected_vdi_ref, vm_ref) # Hide the dialog self.builder.get_object("dialogdeletevdi").hide() openxenmanager/README0000644000175000017500000000253411636446664013167 0ustar rrsrrsOpenXenManager introduction =========================== OpenXenManager is a full-featured graphical interface to manage Citrix XenServer / Xen Cloud Platform (XCP) hosts through the network. OpenXenManager is an open-source multiplatform clone of Citrix XenCenter. It is written in Python, using pyGTK for its interface. The homepage for OpenXenManager is at: https://sourceforge.net/projects/openxenmanager/ Subscribe to the openxenmanager-announce mailing list for important information and release announcements: https://lists.sourceforge.net/lists/listinfo/openxenmanager-announce Running OpenXenManager ====================== To launch OpenXenManager simply run the "openxenmanager" script. Requirements: * Python 2.4 * pyGTK 2.16 * GTK-VNC (Linux only) OpenXenManager runs on Windows or Linux. Mac is not officially supported, though you may be able to get it to work. Help / bug reports ================== If you have found a bug, please file a detailed report in our bug tracker: http://sourceforge.net/tracker/?group_id=327385&atid=1374139 For help you can: * Visit the forums: http://sourceforge.net/projects/openxenmanager/forums * Send an email in the mailing list: https://lists.sourceforge.net/lists/listinfo/openxenmanager-users * Or drop in to our IRC channel: #openxenmanager on irc.freenode.net - Cheng Sun openxenmanager/window_host_network.py0000644000175000017500000001670611636446664017004 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import re class oxcWindowHostNetwork: """ Class to manage "network" tab on host """ def on_treehostnetwork_button_press_event(self, widget, event): """ Function called when button is pressed on network treeview """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo iter = self.builder.get_object("listhostnetwork").get_iter(path) # Get the network reference ref = self.builder.get_object("listhostnetwork").get_value(iter, 7) # Get all pifs on selected network pifs = self.xc_servers[self.selected_host].all_network[ref]['PIFs'] # By default all networks could be removed self.builder.get_object("bthostnetworkremove").set_sensitive(True) for pif in pifs: if self.xc_servers[self.selected_host].all_pif[pif]['physical'] == True: # Except if network is physical self.builder.get_object("bthostnetworkremove").set_sensitive(False) break def on_bthostnetworkadd_clicked(self, widget, data=None): """ Function called when "Add Network..." is pressed """ # Show new network dialog self.builder.get_object("newnetwork").show() # Set default texts self.builder.get_object("txtnetworkname").set_text("New Network") self.builder.get_object("txtnetworkdesc").set_text("") listnetworknic = self.builder.get_object("listnetworknic") # Fill the possible nics for this network, returns the first vlan free vlan = self.xc_servers[self.selected_host].fill_listnetworknic(listnetworknic) # Select the first as default self.builder.get_object("combonetworknic").set_active(0) # Set the first vlan free self.builder.get_object("spinnetworkvlan").set_value(vlan) def on_acceptdialogdeletehostnetwork_clicked(self, widget, data=None): """ Function called when you accept the confirmation dialog to delete a network """ listhostnetwork = self.builder.get_object("listhostnetwork") treehostnetwork = self.builder.get_object("treehostnetwork") # Get selected network selection = treehostnetwork.get_selection() if selection.get_selected()[1] != None: iter = selection.get_selected()[1] ref = listhostnetwork.get_value(iter,7) # Call to function to remove selected network self.xc_servers[self.selected_host].delete_network(ref, self.selected_ref) # Hide the confirmation dialog self.builder.get_object("dialogdeletehostnetwork").hide() def on_canceldialogdeletehostnetwork_clicked(self, widget, data=None): """ Function called when you cancel the confirmation dialog to delete a network """ # Hide the confirmation dialog self.builder.get_object("dialogdeletehostnetwork").hide() def on_acceptnewnetwork_clicked(self, widget, data=None): """ Function called when you accept the "new network" window """ if self.builder.get_object("radioexternalnetwork").get_active(): # External Network # Get text typed name = self.builder.get_object("txtnetworkname").get_text() desc = self.builder.get_object("txtnetworkdesc").get_text() auto = self.builder.get_object("checkautonetwork").get_active() listnetworknic = self.builder.get_object("listnetworknic") combonetworknic = self.builder.get_object("combonetworknic") iter = listnetworknic.get_iter((combonetworknic.get_active(),0)) # Get the pif selected pif = self.builder.get_object("listnetworknic").get_value(iter,0) # And the vlan selected vlan = int(self.builder.get_object("spinnetworkvlan").get_value()) # Call to function to create a external network self.xc_servers[self.selected_host].create_external_network(name, desc, auto, pif, vlan) else: # Internal Network # Get text typed name = self.builder.get_object("txtnetworkname").get_text() desc = self.builder.get_object("txtnetworkdesc").get_text() auto = self.builder.get_object("checkautonetwork").get_active() # Call to function to create a internal network self.xc_servers[self.selected_host].create_internal_network(name, desc, auto) self.builder.get_object("newnetwork").hide() def on_cancelnewnetwork_clicked(self, widget, data=None): """ Function called when you accept the "new network" dialog """ self.builder.get_object("newnetwork").hide() def on_spinnetworkvlan_change_value(self, widget): """ Function called when you changes the value on "vlan" """ data = self.builder.get_object("spinnetworkvlan").get_value() # Checks if selected vlan is available if self.xc_servers[self.selected_host].is_vlan_available(data): # If is available hide the alert text self.builder.get_object("lblvlaninuse").hide() # And enable accept button self.builder.get_object("acceptnewnetwork").set_sensitive(True) else: # If is unavailable show the alert text self.builder.get_object("lblvlaninuse").show() # And disable accept button self.builder.get_object("acceptnewnetwork").set_sensitive(False) def on_bthostnetworkremove_clicked(self, widget, data=None): """ Function called when you click in "Remove network" button on selected network """ self.builder.get_object("dialogdeletehostnetwork").show() def on_canceladdnetwork_clicked(self, widget, data=None): """ Function called when you cancel "Add network" dialog """ self.builder.get_object("dialogaddnetwork").hide() def on_radiointernalnetwork_toggled(self, widget, data=None): """ Function called when you select "create external network" or "create internal network" """ # If external is selected, enable follow widgets for wid in ["label255", "label256", "combonetworknic", "label257", \ "label258", "spinnetworkvlan"]: self.builder.get_object(wid).set_sensitive(self.builder.get_object("radioexternalnetwork").get_active()) openxenmanager/messages.py0000644000175000017500000000602311636446664014465 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # Copyright (C) 2011 Cheng Sun # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- messages = {} messages_header = {} messages_header['PBD_PLUG_FAILED_ON_SERVER_START'] = "Failed to attach storage on server boot" messages['PBD_PLUG_FAILED_ON_SERVER_START'] = "\ A storage repository could not be attached when server '%s' started.\n \ You may be able to fix this using the 'Repair Storage'\n \ option in the Storage menu." messages_header['HOST_SYNC_DATA_FAILED'] = "XenServer statistics synchronization failed" messages['HOST_SYNC_DATA_FAILED'] = "\ %s. There was a temporary failure synchronizing performance statistics across the\n \ pool, probably because one or more servers were offline. Another\n \ synchronization attempt will be made later." messages_header['host_alert_fs_usage'] = "File System On %s Full" messages['host_alert_fs_usage'] = "\ Disk usage for the %s on server '%s' has reached %0.2f%%. XenServer's\n \ performance will be critically affected if this disk becomes full.\n \ Log files or other non-essential (user created) files should be removed." messages_header['alert_cpu_usage'] = "CPU Usage Alarm" messages['alert_cpu_usage'] = "\ CPU usage on VM '%s' has been on average %0.2f%% for the last %d seconds.\n\ This alarm is set to be triggered when CPU usage is more than %0.1f%%" messages_header['VM_SHUTDOWN'] = "VM shutdown" messages['VM_SHUTDOWN'] = "\ VM '%s' has shut down." messages_header['VM_STARTED'] = "VM started" messages['VM_STARTED'] = "\ VM '%s' has started." messages_header['VM_REBOOTED'] = "VM rebooted" messages['VM_REBOOTED'] = "\ VM '%s' has rebooted." messages_header['VM_SUSPENDED'] = "VM suspended" messages['VM_SUSPENDED'] = "\ VM '%s' has suspended." messages_header['VM_RESUMEND'] = "VM resumed" messages['VM_RESUMED'] = "\ VM '%s' has resumed." messages_header['restartHost'] = "After applying this update, all servers must be restarted." messages_header['restartHVM'] = "After applying this update, all Linux VMs must be restarted." messages_header['restartPV'] = "After applying this update, all Windows VMs must be restarted." messages_header['restartXAPI'] = "After applying this update, all VMs must be restarted." openxenmanager/oxcSERVER_properties.py0000644000175000017500000003573111636446664016662 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERproperties: def get_vbd(self, ref): return self.all_vbd[ref] def get_vdi(self, ref): if ref in self.all_vdi: return self.all_vdi[ref] else: return {} def get_storage(self, ref): return self.all_storage[ref] def get_allowed_vbd_devices(self, ref): return self.connection.VM.get_allowed_VBD_devices(self.session_uuid, ref)['Value'] def set_vm_affinity(self, ref, affinity): res = self.connection.VM.set_affinity(self.session_uuid, ref, affinity) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_network_name_label(self, ref, name): res = self.connection.network.set_name_label(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_network_name_description(self, ref, desc): res = self.connection.network.set_name_description(self.session_uuid, ref, desc) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vdi_other_config(self, ref, other_config): res = self.connection.VDI.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vdi_name_label(self, ref_vdi, name): res = self.connection.VDI.set_name_label(self.session_uuid, ref_vdi, name) if "Value" in res: self.track_tasks[res['Value']] = ref_vdi else: print res def set_vdi_name_description(self, ref_vdi, desc): res = self.connection.VDI.set_name_description(self.session_uuid, ref_vdi, desc) if "Value" in res: self.track_tasks[res['Value']] = ref_vdi else: print res def resize_vdi(self, ref_vdi, size): res = self.connection.VDI.resize(self.session_uuid, ref_vdi, str(int(size))) if "Value" in res: self.track_tasks[res['Value']] = ref_vdi else: print res def set_vbd_userdevice(self, ref_vbd, userdevice): res = self.connection.VBD.set_userdevice(self.session_uuid, ref_vbd, userdevice) if "Value" in res: self.track_tasks[res['Value']] = ref_vbd else: print res def set_vbd_mode(self, ref_vbd, mode): res = self.connection.VBD.set_mode(self.session_uuid, ref_vbd, mode) if "Value" in res: self.track_tasks[res['Value']] = ref_vbd else: print res def set_vbd_bootable(self, ref_vbd, bootable): res = self.connection.VBD.set_bootable(self.session_uuid, ref_vbd, bootable) if "Value" in res: self.track_tasks[res['Value']] = ref_vbd else: print res def set_network_other_config(self, ref, other_config): res = self.connection.network.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_network_automatically(self, ref, auto): other_config = {'automatic' : str(auto).lower() } res = self.connection.network.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_host_other_config(self, ref, other_config): res = self.connection.host.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_host_name_label(self, ref, name): res = self.connection.host.set_name_label(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_host_name_description(self, ref, desc): res = self.connection.host.set_name_description(self.session_uuid, ref, desc) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_host_log_destination(self, ref, dest): if dest: log = { 'syslog_destination': dest, } else: log = {} res = self.connection.host.set_logging(self.session_uuid, ref, log) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.host.syslog_reconfigure(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_storage_other_config(self, ref, other_config): res = self.connection.SR.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_storage_name_label(self, ref, name): res = self.connection.SR.set_name_label(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_storage_name_description(self, ref, desc): res = self.connection.SR.set_name_description(self.session_uuid, ref, desc) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_pool_other_config(self, ref, other_config): res = self.connection.pool.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_pool_name_label(self, ref, name): res = self.connection.pool.set_name_label(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_pool_name_description(self, ref, desc): res = self.connection.pool.set_name_description(self.session_uuid, ref, desc) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_other_config(self, ref, other_config): res = self.connection.VM.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_name_label(self, ref, name): res = self.connection.VM.set_name_label(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_name_description(self, ref, desc): res = self.connection.VM.set_name_description(self.session_uuid, ref, desc) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_memory(self, ref, memory): res = self.connection.VM.set_memory_limits(self.session_uuid, ref, str(16777216), str(int(memory*1024*1024)), str(int(memory*1024*1024)), str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: if res["ErrorDescription"][0] == "MESSAGE_METHOD_UNKNOWN": res = self.connection.VM.set_memory_static_min(self.session_uuid, ref, str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.VM.set_memory_dynamic_min(self.session_uuid, ref, str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.VM.set_memory_static_max(self.session_uuid, ref, str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.VM.set_memory_dynamic_max(self.session_uuid, ref, str(int(memory*1024*1024))) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res else: print res def set_vm_vcpus(self, ref, vcpus): res = self.connection.VM.set_VCPUs_at_startup(self.session_uuid, ref, str(int(vcpus))) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_prio(self, ref, prio): prio = { 'weight': str(int(prio)) } res = self.connection.VM.set_VCPUs_params(self.session_uuid, ref, prio) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_poweron(self, ref, poweron): other_config = self.all_vms[ref]['other_config'] other_config["auto_poweron"] = str(poweron).lower() res = self.connection.VM.set_other_config(self.session_uuid, ref, other_config) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_bootpolicy(self, ref, bootpolicy): res = self.connection.VM.set_PV_args(self.session_uuid, ref, bootpolicy) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_memory_multiplier(self, ref, multiplier): res = self.connection.VM.set_HVM_shadow_multiplier(self.session_uuid, ref, float(multiplier)) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_vm_boot_params(self, ref, order): boot_params = self.all_vms[ref]['HVM_boot_params'] boot_params["order"] = order res = self.connection.VM.set_HVM_boot_params(self.session_uuid, ref, boot_params) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def set_pool_custom_fields(self, xml): pool_ref = self.all_pools.keys()[0] self.connection.pool.remove_from_gui_config(self.session_uuid, pool_ref, "XenCenter.CustomFields") res = self.connection.pool.add_to_gui_config(self.session_uuid, pool_ref, "XenCenter.CustomFields", xml) if "Value" in res: self.all_pools[pool_ref]["gui_config"]["XenCenter.CustomFields"] = xml self.track_tasks[res['Value']] = pool_ref else: print res def fill_listcustomfields(self, clist): clist.clear() pool_ref = self.all_pools.keys()[0] if "XenCenter.CustomFields" in self.all_pools[pool_ref]["gui_config"]: dom = xml.dom.minidom.parseString( self.all_pools[pool_ref]["gui_config"]["XenCenter.CustomFields"]) for node in dom.getElementsByTagName("CustomFieldDefinition"): name = node.attributes.getNamedItem("name").value ctype = node.attributes.getNamedItem("type").value clist.append((["%s (%s)" % (name, str(ctype)), name, ctype])) def fill_listhomeserver(self, list, ref): list.clear() path = 0 i = 0 for host in self.all_hosts.keys(): resident_vms = self.all_hosts[host]['resident_VMs'] host_memory = 0 vm_memory = 0 for resident_vm_uuid in resident_vms: if self.all_vms[resident_vm_uuid]['is_control_domain']: host_memory = int(self.all_vms[resident_vm_uuid]['memory_dynamic_max']) else: vm_memory += int(self.all_vms[resident_vm_uuid]['memory_dynamic_max']) host_metrics_uuid = self.all_hosts[host]['metrics'] host_metrics = self.all_host_metrics[host_metrics_uuid] hostmemory = "%s free of %s available (%s total)" % \ (self.convert_bytes(int(host_metrics['memory_total'])-vm_memory-host_memory), \ self.convert_bytes(int(host_metrics['memory_total']) - host_memory), \ self.convert_bytes(host_metrics['memory_total'])) if self.all_hosts[host]['enabled']: if host == ref: path = i list.append([host, gtk.gdk.pixbuf_new_from_file\ ("images/tree_connected_16.png"), self.all_hosts[host]['name_label'], hostmemory, ]) i = i + 1 return path def fill_vdi_location(self, ref, list): list.clear() i = 0 selected = 0 for sr in self.all_storage.keys(): if self.all_storage[sr]['name_label'] != "XenServer Tools": if sr == ref: selected = i name = "" + self.all_storage[sr]['name_label'] + "" else: name = self.all_storage[sr]['name_label'] if len(self.all_storage[sr]['PBDs']) == 0 or self.all_pbd[self.all_storage[sr]['PBDs'][0]]['currently_attached'] == False \ or len(self.all_storage[sr]['PBDs']) > 0 and self.all_storage[sr]["allowed_operations"].count("unplug") == 0: list.append([sr, \ gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png"),\ name]) else: if sr == self.default_sr: list.append([sr, \ gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"),\ name]) else: list.append([sr,\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ name]) i = i +1 return selected openxenmanager/window_vm_performance.py0000644000175000017500000001002311636446664017243 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import os import gtk from threading import Thread import time class oxcWindowVMPerformance: """ Class to manage "performance" of a VM/host """ vport = None prevmousex = 0 prevmousey = 0 toggling = False graphtime = 0 def on_btgraph_clicked(self, widget, data=None): """ Update period time """ times = { "btgraphtenmin" : 5, "btgraphtwohours" : 60, "btgraphoneweek" : 3600, "btgraphoneyear" : 86400 } host = self.selected_host ref = self.selected_ref if self.selected_type == "vm": self.builder.get_object("scrolledwindow50").show() self.builder.get_object("labeldiskusage").show() Thread(target=self.xc_servers[host].update_performance, args=(self.selected_uuid, ref, \ self.selected_ip, False, times[gtk.Buildable.get_name(widget)])).start() else: self.builder.get_object("scrolledwindow50").hide() self.builder.get_object("labeldiskusage").hide() Thread(target=self.xc_servers[host].update_performance, args=(self.selected_uuid, ref, \ self.selected_ip, True, times[gtk.Buildable.get_name(widget)])).start() def on_viewportperf_button_press_event(self, widget, event): """ Function called when you press on image """ self.vport = widget if event.button == 1: # Set cursor and set actual X/Y widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) self.prevmousex = event.x_root self.prevmousey = event.y_root def on_viewportperf_button_release_event(self, widget, event): """ Function called on release mouse """ self.vport = None if event.button == 1: # Disable cursor widget.window.set_cursor(None) def on_viewportperf_motion_notify_event(self, widget, event): """ Function called on mouse move """ if event.is_hint: x, y, state = event.window.get_pointer() else: state = event.state x, y = event.x_root, event.y_root if state & gtk.gdk.BUTTON1_MASK: offset_x = self.prevmousex - x offset_y = self.prevmousey - y self.move_image(offset_x, offset_y) self.prevmousex = x self.prevmousey = y def move_image(self, offset_x, offset_y): """ Move image """ vport = self.vport xadjust = vport.props.hadjustment newx = xadjust.value + offset_x yadjust = vport.props.vadjustment newy = yadjust.value + offset_y if (newx >= xadjust.lower) and \ (newx <= (xadjust.upper - xadjust.page_size)): xadjust.value = newx vport.set_hadjustment(xadjust) if (newy >= yadjust.lower) and \ (newy <= (yadjust.upper - yadjust.page_size)): yadjust.value = newy vport.set_vadjustment(yadjust) openxenmanager/put.py0000644000175000017500000001727711636446664013503 0ustar rrsrrs#!/usr/bin/env python """ put.py - Python HTTP PUT Client Copyright 2006, Sean B. Palmer, inamidst.com Licensed under the Eiffel Forum License 2. Basic API usage, once with optional auth: import put put.putname('test.txt', 'http://example.org/test') f = open('test.txt', 'rb') put.putfile(f, 'http://example.org/test') f.close() bytes = open('test.txt', 'rb').read() auth = {'username': 'myuser', 'password': 'mypass'} put.put(bytes, 'http://example.org/test', **auth) """ import sys, httplib, urlparse from optparse import OptionParser # True by default when running as a script # Otherwise, we turn the noise off... verbose = False def barf(msg): print >> sys.stderr, "Error! %s" % msg sys.exit(1) if sys.version_info < (2, 4): barf("Requires Python 2.4+") def parseuri(uri): """Parse URI, return (host, port, path) tuple. >>> parseuri('http://example.org/testing?somequery#frag') ('example.org', 80, '/testing?somequery') >>> parseuri('http://example.net:8080/test.html') ('example.net', 8080, '/test.html') """ scheme, netplace, path, query, fragid = urlparse.urlsplit(uri) if ':' in netplace: host, port = netplace.split(':', 2) port = int(port) else: host, port = netplace, 80 if query: path += '?' + query return host, port, path def putfile(f, uri, username=None, password=None): """HTTP PUT the file f to uri, with optional auth data.""" host, port, path = parseuri(uri) redirect = set([301, 302, 307]) authenticate = set([401]) okay = set([200, 201, 204]) authorized = False authorization = None tries = 0 while True: # Attempt to HTTP PUT the data h = httplib.HTTPConnection(host, port) h.putrequest('PUT', path) h.putheader('User-Agent', 'put.py/1.0') h.putheader('Connection', 'keep-alive') h.putheader('Transfer-Encoding', 'chunked') h.putheader('Expect', '100-continue') h.putheader('Accept', '*/*') if authorization: h.putheader('Authorization', authorization) h.endheaders() # Chunked transfer encoding # Cf. 'All HTTP/1.1 applications MUST be able to receive and # decode the "chunked" transfer-coding' # - http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html while True: bytes = f.read(1024) if not bytes: break length = len(bytes) #h.send('%X\r\n' % length) h.send(bytes) #h.send('0\r\n\r\n') resp = h.getresponse() status = resp.status # an int # Got a response, now decide how to act upon it if status in redirect: location = resp.getheader('Location') uri = urlparse.urljoin(uri, location) host, port, path = parseuri(uri) # We may have to authenticate again if authorization: authorization = None elif status in authenticate: # If we've done this already, break if authorization: # barf("Going around in authentication circles") barf("Authentication failed") if not (username and password): barf("Need a username and password to authenticate with") # Get the scheme: Basic or Digest? wwwauth = resp.msg['www-authenticate'] # We may need this again wauth = wwwauth.lstrip(' \t') # Hence use wauth not wwwauth here wauth = wwwauth.replace('\t', ' ') i = wauth.index(' ') scheme = wauth[:i].lower() if scheme in set(['basic', 'digest']): if verbose: msg = "Performing %s Authentication..." % scheme.capitalize() print >> sys.stderr, msg else: barf("Unknown authentication scheme: %s" % scheme) if scheme == 'basic': import base64 userpass = username + ':' + password userpass = base64.encodestring(userpass).strip() authorized, authorization = True, 'Basic ' + userpass elif scheme == 'digest': if verbose: msg = "uses fragile, undocumented features in urllib2" print >> sys.stderr, "Warning! Digest Auth %s" % msg import urllib2 # See warning above passwd = type('Password', (object,), { 'find_user_password': lambda self, *args: (username, password), 'add_password': lambda self, *args: None })() req = type('Request', (object,), { 'get_full_url': lambda self: uri, 'has_data': lambda self: None, 'get_method': lambda self: 'PUT', 'get_selector': lambda self: path })() # Cf. urllib2.AbstractDigestAuthHandler.retry_http_digest_auth auth = urllib2.AbstractDigestAuthHandler(passwd) token, challenge = wwwauth.split(' ', 1) chal = urllib2.parse_keqv_list(urllib2.parse_http_list(challenge)) userpass = auth.get_authorization(req, chal) authorized, authorization = True, 'Digest ' + userpass elif status in okay: if (username and password) and (not authorized): msg = "Warning! The supplied username and password went unused" print >> sys.stderr, msg if verbose: resultLine = "Success! Resource %s" statuses = {200: 'modified', 201: 'created', 204: 'modified'} print resultLine % statuses[status] statusLine = "Response-Status: %s %s" print statusLine % (status, resp.reason) body = resp.read(58) body = body.rstrip('\r\n') body = body.encode('string_escape') if len(body) >= 58: body = body[:57] + '[...]' bodyLine = 'Response-Body: "%s"' print bodyLine % body break # @@ raise PutError, do the catching in main? else: barf('Got "%s %s"' % (status, resp.reason)) tries += 1 if tries >= 50: barf("Too many redirects") return status, resp def putname(fn, uri, username=None, password=None): """HTTP PUT the file with filename fn to uri, with optional auth data.""" auth = {'username': username, 'password': password} if fn != '-': f = open(fn, 'rb') status, resp = putfile(f, uri, **auth) f.close() else: status, resp = putfile(sys.stdin, uri, **auth) return status, resp def put(s, uri, username=None, password=None): """HTTP PUT the string s to uri, with optional auth data.""" try: from cStringIO import StringIO except ImportError: from StringIO import StringIO f = StringIO(s) f.seek(0) status, resp = putfile(f, uri, username=username, password=password) f.close() return status, conn def main(argv=None): usage = ('%prog [options] filename uri\n' + 'The filename may be - for stdin\n' + 'Use command line password at your own risk!') parser = OptionParser(usage=usage) parser.add_option('-u', '--username', help='HTTP Auth username') parser.add_option('-p', '--password', help='HTTP Auth password') parser.add_option('-q', '--quiet', action='store_true', help="shhh!") options, args = parser.parse_args(argv) if len(args) != 2: parser.error("Requires two arguments, filename and uri") fn, uri = args if not uri.startswith('http:'): parser.error("The uri argument must start with 'http:'") if ((options.username and not options.password) or (options.password and not options.username)): parser.error("Must have both username and password or neither") global verbose verbose = (not options.quiet) auth = {'username': options.username, 'password': options.password} putname(fn, uri, **auth) if __name__ == '__main__': main() openxenmanager/window_vm_storage.py0000644000175000017500000003116211636446664016415 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python class oxcWindowVMStorage: """ Class to manage storage tab in a VM """ def on_btcancelattachdisk_clicked(self, widget, data=None): """ Function called when you cancel the "attack disk" window """ # Hide the dialog self.builder.get_object("vmattachdisk").hide() def on_btacceptattachdisk_clicked(self, widget, data=None): """ Function called when you accept the "attack disk" window """ treeattachdisk = self.builder.get_object("treeattachdisk") listattachdisk = self.builder.get_object("listattachdisk") iter_ref= treeattachdisk.get_selection().get_selected()[1] # Get if "read only" is checked ro = self.builder.get_object("checkattachdisk").get_active() # Get selected disk disk = listattachdisk.get_value(iter_ref, 1) vm = self.selected_ref # Attack the selected disk self.xc_servers[self.selected_host].attach_disk_to_vm(vm, disk, ro) # Hide the dialog self.builder.get_object("vmattachdisk").hide() def on_cancelnewvmdisk_clicked(self, widget, data=None): """ Function called when you cancel the "new disk" window """ # Hide the dialog self.builder.get_object("newvmdisk").hide() def on_addnewvmstorage_clicked(self, widget, data=None): """ Function called when you press "add new storage" """ self.newvmdata['storage_editing'] = False # Show the dialog self.builder.get_object("newvmdisk").show() def on_btstorageattach_clicked(self, widget, data=None): """ Function called when you press "Attach disk" """ self.builder.get_object("vmattachdisk").show() listattachdisk = self.builder.get_object("listattachdisk") # Fill the possible disks to attach self.xc_servers[self.selected_host].fill_vm_storageattach(listattachdisk) # Expand list self.builder.get_object("treeattachdisk").expand_all() def on_btstoragedelete_clicked(self, widget, data=None): """ Function called when you press "Delete disk" """ # Show a confimration dialog self.builder.get_object("dialogdeletedisk").show() def on_canceldeletedisk_clicked(self, widget, data=None): """ Function called when you cancel "delete disk" confirmation dialog """ self.builder.get_object("dialogdeletedisk").hide() def on_acceptdeletedisk_clicked(self, widget, data=None): """ Function called when you accept "delete disk" confirmation dialog """ treevmstorage = self.builder.get_object("treevmstorage") listvmstorage = self.builder.get_object("listvmstorage") selection = treevmstorage.get_selection() if selection.get_selected()[1] == None: self.builder.get_object("btstorageproperties").set_sensitive(False) iter_ref= listvmstorage.get_iter((0,)) else: self.builder.get_object("btstorageproperties").set_sensitive(True) iter_ref= selection.get_selected()[1] # Get selected disk vdi = listvmstorage.get_value(iter_ref, 9) # Delete it self.xc_servers[self.selected_host].delete_vdi(vdi, self.selected_ref) # Select the first of a list treevmstorage.set_cursor((0,), treevmstorage.get_column(0)) treevmstorage.get_selection().select_path((0, 0)) # Hide the dialog self.builder.get_object("dialogdeletedisk").hide() def on_btstoragedeactivate_clicked(self, widget, data=None): """ Function called when you press "Deactivate"/"Activate" button """ listvmstorage = self.builder.get_object("listvmstorage") treevmstorage = self.builder.get_object("treevmstorage") # Get selected disk iter_ref= treevmstorage.get_selection().get_selected()[1] # Dettach disk if widget.get_label() == "Activate": self.xc_servers[self.selected_host].vm_storageplug(listvmstorage.get_value(iter_ref,10)) else: self.xc_servers[self.selected_host].vm_storageunplug(listvmstorage.get_value(iter_ref,10)) def on_btstoragedetach_clicked(self, widget, data=None): """ Function called when you press "Detach" button """ listvmstorage = self.builder.get_object("listvmstorage") treevmstorage = self.builder.get_object("treevmstorage") # Get selected disk iter_ref= treevmstorage.get_selection().get_selected()[1] # Dettach disk self.xc_servers[self.selected_host].vm_storagedetach(listvmstorage.get_value(iter_ref,10)) def on_treeattachdisk_cursor_changed(self, widget, data=None): """ Function called when you select a disk to attach """ treeattachdisk = self.builder.get_object("treeattachdisk") listattachdisk = self.builder.get_object("listattachdisk") iter_ref= treeattachdisk.get_selection().get_selected()[1] # Element fourth from list indicates if disk could be used to attach # Then enable or disable button depends its value self.builder.get_object("btacceptattachdisk").set_sensitive(listattachdisk.get_value(iter_ref, 4)) def on_btstorageproperties_clicked(self, widget, data=None): """ Function not used """ listvmstorage = self.builder.get_object("listvmstorage") treevmstorage = self.builder.get_object("treevmstorage") iter_ref= treevmstorage.get_selection().get_selected()[1] ref = listvmstorage.get_value(iter_ref,10) print self.xc_servers[self.selected_host].get_allowed_vbd_devices(self.selected_ref) def on_combovmstoragedvd_changed(self, widget, data=None): """ Function called when you change the "DVD Drive" combobox """ if widget.get_active_iter() != None and self.set_active == False: # If a element is selected.. # Get the element vdi = self.builder.get_object("listvmstoragedvd").get_value(widget.get_active_iter(), 1) # And call to function "set_vm_dvd" to insert into VM self.xc_servers[self.selected_host].set_vm_dvd(self.selected_ref, vdi) def on_btvmaddstorage_clicked(self, widget, data=None): """ Function called when you press the "New disk" button """ vmaddnewdisk = self.builder.get_object("vmaddnewdisk") # Set a default name self.builder.get_object("vmaddnewdisk_name").set_text("New virtual disk on " + self.selected_name) self.builder.get_object("vmaddnewdisk_desc").set_text("") listnewvmdisk1 = self.builder.get_object("listnewvmdisk1") # Fill the possible storage and return the defalt defsr = self.xc_servers[self.selected_host].fill_listnewvmdisk(listnewvmdisk1, self.selected_host) # Set the cursor on default storage treenewvmstorage1 = self.builder.get_object("treenewvmdisk1") treenewvmstorage1.set_cursor((defsr,), treenewvmstorage1.get_column(0)) treenewvmstorage1.get_selection().select_path((defsr, 0)) # Set as default 5GB self.builder.get_object("disksize2").set_value(float(5)) # Show the window vmaddnewdisk.show() def on_cancelvmaddnewdisk_clicked(self, widget, data=None): """ Function called when you press "cancel" button on "add new disk" window """ self.builder.get_object("vmaddnewdisk").hide() def on_treevmstorage_cursor_changed(self, widget, data=None): """ Function called when you select a storage on storage tree """ treevmstorage = self.builder.get_object("treevmstorage") listvmstorage = self.builder.get_object("listvmstorage") selection = treevmstorage.get_selection() # If some storage is selected, then enable "properties window" if selection.get_selected()[1] == None: self.builder.get_object("btstorageproperties").set_sensitive(False) iter_ref= listvmstorage.get_iter((0,)) else: self.builder.get_object("btstorageproperties").set_sensitive(True) iter_ref= selection.get_selected()[1] # Get selected disk (vdi) vdi = listvmstorage.get_value(iter_ref, 9) vdi_info = self.xc_servers[self.selected_host].all_vdi[vdi] vbd_info = self.xc_servers[self.selected_host].all_vbd[vdi_info['VBDs'][0]] # Depends the type of disk and if is attached, enable or disable buttons if vdi_info['type'] == "user": self.builder.get_object("btstoragedeactivate").set_sensitive(True) if vbd_info['currently_attached']: self.builder.get_object("btstoragedeactivate").set_label("Deactivate") else: self.builder.get_object("btstoragedeactivate").set_label("Activate") self.builder.get_object("btstoragedetach").set_sensitive(True) else: self.builder.get_object("btstoragedeactivate").set_sensitive(False) self.builder.get_object("btstoragedetach").set_sensitive(False) if vdi_info['allowed_operations'].count("destroy") > 0: self.builder.get_object("btstoragedelete").set_sensitive(True) else: self.builder.get_object("btstoragedelete").set_sensitive(False) def on_treevmnetwork_cursor_changed(self, widget, data=None): """ Function called when you select a storage on storage tree """ treevmnetwork = self.builder.get_object("treevmnetwork") listvmnetwork = self.builder.get_object("listvmnetwork") selection = treevmnetwork.get_selection() # If some storage is selected, then enable "properties window" if selection.get_selected()[1] == None: self.builder.get_object("btpropertiesinterface").set_sensitive(False) self.builder.get_object("btremoveinterface").set_sensitive(False) iter_ref= listvmnetwork.get_iter((0,)) else: self.builder.get_object("btpropertiesinterface").set_sensitive(True) self.builder.get_object("btremoveinterface").set_sensitive(True) iter_ref = selection.get_selected()[1] # Get selected disk (vdi) vif = listvmnetwork.get_value(iter_ref, 6) vif_info = self.xc_servers[self.selected_host].all_vif[vif] unplug = vif_info["allowed_operations"].count("unplug") self.builder.get_object("btpropertiesinterface").set_sensitive(unplug or self.selected_state == "Halted") self.builder.get_object("btremoveinterface").set_sensitive(unplug or self.selected_state == "Halted") def on_acceptvmaddnewdisk_clicked(self, widget, data=None): """ Function called when you accept the "new disk" window """ treenewvmstorage = self.builder.get_object("treenewvmdisk1") listnewvmdisk1 = self.builder.get_object("listnewvmdisk1") selection = treenewvmstorage.get_selection() # Get selected storage if selection.get_selected()[1] == None: iter_ref= listnewvmdisk1.get_iter((0,1)) else: iter_ref= selection.get_selected()[1] name = self.builder.get_object("vmaddnewdisk_name").get_text() description = self.builder.get_object("vmaddnewdisk_desc").get_text() sr = listnewvmdisk1.get_value(iter_ref, 4) virtual_size = int(self.builder.get_object("disksize2").get_value()*1024*1024*1024) # Add new disk with the selected options (size, name, description..) self.xc_servers[self.selected_host].add_disk_to_vm( name, description, sr, virtual_size, self.selected_uuid, self.selected_ref) self.builder.get_object("vmaddnewdisk").hide() openxenmanager/xtea.py0000644000175000017500000001116211636446664013617 0ustar rrsrrs""" XTEA Block Encryption Algorithm Author: Paul Chakravarti (paul_dot_chakravarti_at_gmail_dot_com) License: Public Domain This module provides a Python implementation of the XTEA block encryption algorithm (http://www.cix.co.uk/~klockstone/xtea.pdf). The module implements the basic XTEA block encryption algortithm (`xtea_encrypt`/`xtea_decrypt`) and also provides a higher level `crypt` function which symmetrically encrypts/decrypts a variable length string using XTEA in OFB mode as a key generator. The `crypt` function does not use `xtea_decrypt` which is provided for completeness only (but can be used to support other stream modes - eg CBC/CFB). This module is intended to provide a simple 'privacy-grade' Python encryption algorithm with no external dependencies. The implementation is relatively slow and is best suited to small volumes of data. Note that the XTEA algorithm has not been subjected to extensive analysis (though is believed to be relatively secure - see http://en.wikipedia.org/wiki/XTEA). For applications requiring 'real' security please use a known and well tested algorithm/implementation. The security of the algorithm is entirely based on quality (entropy) and secrecy of the key. You should generate the key from a known random source and exchange using a trusted mechanism. In addition, you should always use a random IV to seed the key generator (the IV is not sensitive and does not need to be exchanged securely) >>> import os >>> iv = 'ABCDEFGH' >>> z = crypt('0123456789012345','Hello There',iv) >>> z.encode('hex') 'fe196d0a40d6c222b9eff3' >>> crypt('0123456789012345',z,iv) 'Hello There' """ import struct def crypt(key,data,iv='\00\00\00\00\00\00\00\00',n=32): """ Encrypt/decrypt variable length string using XTEA cypher as key generator (OFB mode) * key = 128 bit (16 char) * iv = 64 bit (8 char) * data = string (any length) >>> import os >>> key = os.urandom(16) >>> iv = os.urandom(8) >>> data = os.urandom(10000) >>> z = crypt(key,data,iv) >>> crypt(key,z,iv) == data True """ def keygen(key,iv,n): while True: iv = xtea_encrypt(key,iv,n) for k in iv: yield ord(k) xor = [ chr(x^y) for (x,y) in zip(map(ord,data),keygen(key,iv,n)) ] return "".join(xor) def xtea_encrypt(key,block,n=32,endian="!"): """ Encrypt 64 bit data block using XTEA block cypher * key = 128 bit (16 char) * block = 64 bit (8 char) * n = rounds (default 32) * endian = byte order (see 'struct' doc - default big/network) >>> z = xtea_encrypt('0123456789012345','ABCDEFGH') >>> z.encode('hex') 'b67c01662ff6964a' Only need to change byte order if sending/receiving from alternative endian implementation >>> z = xtea_encrypt('0123456789012345','ABCDEFGH',endian="<") >>> z.encode('hex') 'ea0c3d7c1c22557f' """ v0,v1 = struct.unpack(endian+"2L",block) k = struct.unpack(endian+"4L",key) sum,delta,mask = 0L,0x9e3779b9L,0xffffffffL for round in range(n): v0 = (v0 + (((v1<<4 ^ v1>>5) + v1) ^ (sum + k[sum & 3]))) & mask sum = (sum + delta) & mask v1 = (v1 + (((v0<<4 ^ v0>>5) + v0) ^ (sum + k[sum>>11 & 3]))) & mask return struct.pack(endian+"2L",v0,v1) def xtea_decrypt(key,block,n=32,endian="!"): """ Decrypt 64 bit data block using XTEA block cypher * key = 128 bit (16 char) * block = 64 bit (8 char) * n = rounds (default 32) * endian = byte order (see 'struct' doc - default big/network) >>> z = 'b67c01662ff6964a'.decode('hex') >>> xtea_decrypt('0123456789012345',z) 'ABCDEFGH' Only need to change byte order if sending/receiving from alternative endian implementation >>> z = 'ea0c3d7c1c22557f'.decode('hex') >>> xtea_decrypt('0123456789012345',z,endian="<") 'ABCDEFGH' """ v0,v1 = struct.unpack(endian+"2L",block) k = struct.unpack(endian+"4L",key) delta,mask = 0x9e3779b9L,0xffffffffL sum = (delta * n) & mask for round in range(n): v1 = (v1 - (((v0<<4 ^ v0>>5) + v0) ^ (sum + k[sum>>11 & 3]))) & mask sum = (sum - delta) & mask v0 = (v0 - (((v1<<4 ^ v1>>5) + v1) ^ (sum + k[sum & 3]))) & mask return struct.pack(endian+"2L",v0,v1) if __name__ == "__main__": import doctest doctest.testmod() import os iv = 'OXCENTER' z = crypt('0001001203123','Hello There',iv) print z.encode('hex') print crypt('0123456789012345',z,iv) openxenmanager/oxm.py0000644000175000017500000000026711636446664013465 0ustar rrsrrs# TODO: this will be the future entry-point import pygtk import gobject def idle(func): return lambda *args, **kwargs: gobject.idle_add(lambda: func(*args, **kwargs) and False) openxenmanager/oxcSERVER_menuitem.py0000644000175000017500000006006611636446664016310 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header from capabilities import capabilities_text class oxcSERVERmenuitem: last_pool_data = [] def pause_vm(self, ref): res = self.connection.Async.VM.pause(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def unsuspend_vm(self, ref): res = self.connection.Async.VM.unsuspend(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def resume_vm(self, ref): res = self.connection.Async.VM.resume(self.session_uuid, ref, False, True) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def hard_shutdown_vm(self, ref): res = self.connection.Async.VM.hard_shutdown(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def hard_reboot_vm(self, ref): res = self.connection.Async.VM.hard_reboot(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def start_vm(self, ref): res = self.connection.Async.VM.start(self.session_uuid, ref, False, False) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def start_vm_recovery_mode(self, ref): change_policy = False if not self.all_vms[ref]['HVM_boot_policy']: self.connection.VM.set_HVM_boot_policy(self.session_uuid, ref, "BIOS order") change_policy = True order = "" if "order" in self.all_vms[ref]['HVM_boot_params']: order = self.all_vms[ref]['HVM_boot_params']['order'] self.connection.VM.set_HVM_boot_params(self.session_uuid, ref, {"order": "dn"}) res = self.connection.VM.start(self.session_uuid, ref, False, False) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res if change_policy: self.connection.VM.set_HVM_boot_policy(self.session_uuid, ref, "") self.connection.VM.set_HVM_boot_params(self.session_uuid, ref, {"order": order}) def clean_shutdown_vm(self, ref): res = self.connection.Async.VM.clean_shutdown(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def clean_reboot_vm(self, ref): res = self.connection.Async.VM.clean_reboot(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def can_start(self, ref, host_uuid): can_boot = self.connection.VM.assert_can_boot_here ( self.session_uuid, ref, host_uuid) if "ErrorDescription" in can_boot: return can_boot["ErrorDescription"][0].replace("_","__") else: return "" def suspend_vm(self, ref): res = self.connection.Async.VM.suspend(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def unpause_vm(self, ref): res = self.connection.VM.unpause(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def make_into_template(self, ref): res = self.connection.VM.set_is_a_template(self.session_uuid, ref, True) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def start_vm_on(self, widget, ref, host): res = self.connection.Async.VM.start_on(self.session_uuid, ref, host, False, False) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def resume_vm_on(self, widget, ref, host): res = self.connection.Async.VM.resume_on(self.session_uuid, ref, host, False, False) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def migrate_vm(self, widget, ref, host): res = self.connection.Async.VM.pool_migrate(self.session_uuid, ref, host, {}) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def fill_list_updates(self, ref, list): list.clear() for patch in self.all_pool_patch: list.append([patch, self.all_pool_patch[patch]['name_label']]) def fill_list_report(self, ref, list): list.clear() result = self.connection.host.get_system_status_capabilities(self.session_uuid, ref)['Value'] privacy = {"yes": "1", "maybe": "2", "if_customized": "3", "no": "4"} dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("capability") capabilities = {} for node in nodes: attr = node.attributes key, checked, pii, minsize, maxsize, mintime, maxtime = [attr.getNamedItem(k).value for k \ in ["key", "default-checked", "pii", "min-size", "max-size", "min-time", "max-time"]] capabilities[privacy[pii] + "_" + key] = [checked, minsize, maxsize, mintime, maxtime] for key in sorted(capabilities.keys()): if key.split("_",2)[1] in capabilities_text: confidentiality, ref = key.split("_",2) name, desc = capabilities_text[key.split("_",2)[1]] checked, minsize, maxsize, mintime, maxtime = [value for value in capabilities[key]] size1, time1 = 0, 0 if minsize == maxsize: if maxsize != "-1" and checked: size1 = int(maxsize) size = self.convert_bytes(maxsize) elif minsize == "-1": if checked: size1 = int(maxsize) size = "< %s" % self.convert_bytes(maxsize) else: size1 = int(maxsize) size = "%s-%s" % (self.convert_bytes(minsize), self.convert_bytes(maxsize)) if mintime == maxtime: if maxtime == "-1": time = "Negligible" else: if checked: time1 = int(maxtime) time = maxtime elif mintime == "-1": if checked: time1 = int(maxtime) time= "< %s" % maxtime else: if checked: time1 = int(maxtime) time= "%s-%s" % (mintime, maxtime) list.append([ref, checked == "yes", name, gtk.gdk.pixbuf_new_from_file("images/confidentiality%s.png" % confidentiality), desc, size, time, size1, time1, int(confidentiality)]) def fill_list_templates(self, list): list.clear() for vm in filter(self.filter_custom_template, sorted(self.all_vms.values(), key=itemgetter('name_label'))): self.filter_uuid = vm["uuid"] if vm["is_a_snapshot"]: list.append([gtk.gdk.pixbuf_new_from_file("images/snapshots.png"), vm["name_label"], self.vm_filter_uuid(), "Snapshots"]) else: list.append([gtk.gdk.pixbuf_new_from_file("images/user_template_16.png"), vm["name_label"], self.vm_filter_uuid(), "Custom"]) for vm in filter(self.filter_normal_template, sorted(self.all_vms.values(), key=itemgetter('name_label'))): self.filter_uuid = vm["uuid"] if vm["name_label"].lower().count("centos"): image = "images/centos.png" category = "CentOS" elif vm["name_label"].lower().count("windows"): image = "images/windows.png" category = "Windows" elif vm["name_label"].lower().count("debian"): image = "images/debian.png" category = "Debian" elif vm["name_label"].lower().count("red hat"): image = "images/redhat.png" category = "Red Hat" elif vm["name_label"].lower().count("suse"): image = "images/suse.png" category = "SuSe" elif vm["name_label"].lower().count("oracle"): image = "images/oracle.png" category = "Oracle" elif vm["name_label"].lower().count("citrix"): image = "images/xen.png" category = "Citrix" else: image = "images/template_16.png" category = "Misc" list.append([gtk.gdk.pixbuf_new_from_file(image), vm["name_label"], self.vm_filter_uuid(), category]) def fill_list_isoimages(self, list): list.clear() for sr in self.all_storage: if self.all_storage[sr]['type'] == "iso": list.append([self.all_storage[sr]['name_label'], "", 1, 0]) for vdi in self.all_storage[sr]['VDIs']: list.append(["\t" + self.all_vdi[vdi]['name_label'], vdi, 0, 1]) def fill_list_phydvd(self, list): list.clear() for sr in self.all_storage: if self.all_storage[sr]['type'] == "udev" and self.all_storage[sr]['sm_config']["type"] == "cd": if len(self.all_storage[sr]['PBDs']): vdis = self.all_storage[sr]['VDIs'] for vdi in vdis: list.append(["DVD Drive " + self.all_vdi[vdi]['location'][-1:], vdi]) """ if self.all_pbd[pbd]['host'] == self.wine.selected_ref: list.append([self.all_storage[sr]['name_label'], pbd]) """ def fill_list_networks(self, list, list2): list.clear() list2.clear() i = 0 for network in self.all_network: if self.all_network[network]['bridge'] != "xapi0": if "automatic" in self.all_network[network]['other_config'] and \ self.all_network[network]['other_config']["automatic"] == "true": list.append(["interface " + str(i), "auto-generated", self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network '), network]) i = i + 1 list2.append([self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network '), network]) def fill_management_networks(self, list, network_ref): list.clear() i = 0 current = 0 for network in self.all_network: if self.all_network[network]['bridge'] != "xapi0": if self.all_network[network]['PIFs'] and self.all_pif[self.all_network[network]['PIFs'][0]]['bond_slave_of'] == "OpaqueRef:NULL": if network == network_ref: current = i list.append([network, self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network ')]) i = i + 1 return current def fill_mamagement_ifs_list(self, list): list.clear() for pif in self.all_pif: if self.all_pif[pif]['management']: network = self.all_network[self.all_pif[pif]['network']]['name_label'] if self.all_pif[pif]['device'][-1:] == "0": text = "Primary" + "\n " + network + "" list.append([pif, gtk.gdk.pixbuf_new_from_file("images/prop_networksettings.png"), text]) else: text = "Interface " + str(self.all_pif[pif]['device'][-1:]) + "\n " + network + "" list.append([pif, gtk.gdk.pixbuf_new_from_file("images/prop_network.png"), text]) def fill_listnewvmhosts(self, list): list.clear() path = 0 i = 0 for host in self.all_hosts.keys(): resident_vms = self.all_hosts[host]['resident_VMs'] host_memory = 0 for resident_vm_uuid in resident_vms: if self.all_vms[resident_vm_uuid]['is_control_domain']: host_memory = self.all_vms[resident_vm_uuid]['memory_dynamic_max'] host_metrics_uuid = self.all_hosts[host]['metrics'] host_metrics = self.all_host_metrics[host_metrics_uuid] hostmemory = "%s free of %s available (%s total)" % \ (self.convert_bytes(host_metrics['memory_free']), \ self.convert_bytes(int(host_metrics['memory_total']) - int(host_memory)), \ self.convert_bytes(host_metrics['memory_total'])) if self.all_hosts[host]['enabled']: path = i list.append([gtk.gdk.pixbuf_new_from_file\ ("images/tree_connected_16.png"), self.all_hosts[host]['name_label'], hostmemory, host]) else: list.append([gtk.gdk.pixbuf_new_from_file\ ("images/tree_disconnected_16.png"), self.all_hosts[host]['name_label'], hostmemory, host]) i = i + 1 return path def set_default_storage(self, ref): pool_ref = self.all_pools.keys()[0] res = self.connection.pool.set_default_SR(self.session_uuid, pool_ref, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.pool.set_suspend_image_SR(self.session_uuid, pool_ref, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res res = self.connection.pool.set_crash_dump_SR(self.session_uuid, pool_ref, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def fill_listrepairstorage(self, list, ref): list.clear() for pbd_ref in self.all_storage[ref]['PBDs']: host = self.all_hosts[self.all_pbd[pbd_ref]["host"]]["name_label"] host_ref = self.all_pbd[pbd_ref]["host"] if not self.all_pbd[pbd_ref]['currently_attached']: list.append([pbd_ref, gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png"), host, "Unplugged", host_ref, True]) else: list.append([pbd_ref, gtk.gdk.pixbuf_new_from_file("images//storage_shaped_16.png"), host, "Connected", host_ref, False]) def repair_storage(self, list, ref): error = False for pbd_ref in self.all_storage[ref]['PBDs']: value = self.connection.Async.PBD.plug(self.session_uuid, pbd_ref)["Value"] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if len(task["error_info"]): print task["error_info"] error = True gobject.idle_add(lambda: self.wine.builder.get_object("lblrepairerror").set_markup(\ "Host could not be contacted") and False) for i in range(0, list.__len__()): if list.get_value(list.get_iter((i,)), 0) == pbd_ref: if error: gobject.idle_add(lambda: list.set_value(list.get_iter((i,)), 3, "Unplugged") and False) else: gobject.idle_add(lambda: list.set_value(list.get_iter((i,)), 3, "Connected") and False) if not error: gobjec.idle_add(lambda: self.wine.builder.get_object("lblrepairerror").set_markup(\ "All repaired.") and False) gobject.idle_add(lambda: self.wine.builder.get_object("acceptrepairstorage").set_sensitive(True) and False) gobject.idle_add(lambda: self.wine.builder.get_object("cancelrepairstorage").set_label("Close") and False) def remove_server_from_pool(self, ref): self.connection.pool.eject(self.session_uuid, ref) def shutdown_server(self, ref): res = self.connection.host.disable(self.session_uuid, ref) if "Value" in res: res = self.connection.host.shutdown(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] return "OK" else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) def reboot_server(self, ref): res = self.connection.host.disable(self.session_uuid, ref) if "Value" in res: res = self.connection.host.reboot(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] return "OK" else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) def set_license_host(self, ref, licensehost, licenseport, edition): res = self.connection.host.set_license_server(self.session_uuid, ref, {"address": licensehost, "port": licenseport}) if "Value" in res: res = self.connection.host.apply_edition(self.session_uuid, ref, edition) if not "Value" in res: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) else: #self.wine.builder.get_object("warninglicense").show() self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) def add_server_to_pool(self, widget, ref, server, server_ref, master_ip): self.wine.xc_servers[server].all_hosts[server_ref] user = self.wine.xc_servers[server].user password = self.wine.xc_servers[server].password host = master_ip res = self.wine.xc_servers[server].connection.pool.join(self.session_uuid, host, user, password) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[self.all_hosts.keys()[0]][0] self.last_pool_data = [] self.wine.last_host_pool = None else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) if res["ErrorDescription"][0] == "HOSTS_NOT_HOMOGENEOUS": self.last_pool_data = [server, server_ref, master_ip] self.wine.last_host_pool = server self.wine.builder.get_object("forcejoinpool").show() def add_server_to_pool_force(self, ref, data=None): server = data[0] server_ref = data[1] master_ip = data[2] self.wine.xc_servers[server].all_hosts[server_ref] user = self.wine.xc_servers[server].user password = self.wine.xc_servers[server].password host = master_ip res = self.wine.xc_servers[server].connection.pool.join_force(self.session_uuid, host, user, password) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[self.all_hosts.keys()[0]][0] else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) def delete_pool(self, pool_ref): res = self.connection.pool.set_name_label(self.session_uuid, pool_ref, "") if "Value" in res: self.track_tasks[res['Value']] = pool_ref else: print res master = self.all_pools[pool_ref]['master'] for host in self.all_hosts: if host != master: res = self.connection.pool.eject(self.session_uuid, pool_ref, host) if "Value" in res: self.track_tasks[res['Value']] = pool_ref else: print res def destroy_vm(self, ref, delete_vdi, delete_snap): #FIXME if delete_vdi: if ref in self.all_vms: for vbd in self.all_vms[ref]['VBDs']: if vbd in self.all_vbd and self.all_vbd[vbd]['type'] != "CD": res = self.connection.VBD.destroy(self.session_uuid, vbd) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res if delete_snap: all_snapshots = self.all_vms[ref]['snapshots'] for snap in all_snapshots: self.destroy_vm(snap, True, False) res = self.connection.VM.destroy(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def fill_listcopystg(self, list, host): list.clear() i = 0 default_sr = 0 for sr in self.all_storage.keys(): storage = self.all_storage[sr] if storage['type'] != "iso" and storage['type'] != "udev": if self.default_sr == sr: default_sr = i if not self.all_storage[sr]['PBDs'] or not self.all_pbd[self.all_storage[sr]['PBDs'][0]]['currently_attached'] \ or self.all_storage[sr]['PBDs'] and self.all_storage[sr]["allowed_operations"].count("unplug") == 0: pass else: if self.default_sr == sr: list.append([gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"), sr, storage['name_label'], self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])) + " free of " + \ self.convert_bytes(storage['physical_size'])]) else: list.append([gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"), sr, storage['name_label'], self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])) + " free of " + \ self.convert_bytes(storage['physical_size'])]) """ else: FIXME: set_sensitive(False) row list.append([gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png"), sr, storage['name_label'], self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])) + " free of " + \ self.convert_bytes(storage['physical_size'])]) else: """ i = i + 1 return default_sr openxenmanager/configobj.py0000644000175000017500000025270611636446664014631 0ustar rrsrrs# configobj.py # A config file reader/writer that supports nested sections in config files. # Copyright (C) 2005-2010 Michael Foord, Nicola Larosa # E-mail: fuzzyman AT voidspace DOT org DOT uk # nico AT tekNico DOT net # ConfigObj 4 # http://www.voidspace.org.uk/python/configobj.html # Released subject to the BSD License # Please see http://www.voidspace.org.uk/python/license.shtml # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml # For information about bugfixes, updates and support, please join the # ConfigObj mailing list: # http://lists.sourceforge.net/lists/listinfo/configobj-develop # Comments, suggestions and bug reports welcome. from __future__ import generators import os import re import sys from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE # imported lazily to avoid startup performance hit if it isn't used compiler = None # A dictionary mapping BOM to # the encoding to decode with, and what to set the # encoding attribute to. BOMS = { BOM_UTF8: ('utf_8', None), BOM_UTF16_BE: ('utf16_be', 'utf_16'), BOM_UTF16_LE: ('utf16_le', 'utf_16'), BOM_UTF16: ('utf_16', 'utf_16'), } # All legal variants of the BOM codecs. # TODO: the list of aliases is not meant to be exhaustive, is there a # better way ? BOM_LIST = { 'utf_16': 'utf_16', 'u16': 'utf_16', 'utf16': 'utf_16', 'utf-16': 'utf_16', 'utf16_be': 'utf16_be', 'utf_16_be': 'utf16_be', 'utf-16be': 'utf16_be', 'utf16_le': 'utf16_le', 'utf_16_le': 'utf16_le', 'utf-16le': 'utf16_le', 'utf_8': 'utf_8', 'u8': 'utf_8', 'utf': 'utf_8', 'utf8': 'utf_8', 'utf-8': 'utf_8', } # Map of encodings to the BOM to write. BOM_SET = { 'utf_8': BOM_UTF8, 'utf_16': BOM_UTF16, 'utf16_be': BOM_UTF16_BE, 'utf16_le': BOM_UTF16_LE, None: BOM_UTF8 } def match_utf8(encoding): return BOM_LIST.get(encoding.lower()) == 'utf_8' # Quote strings used for writing values squot = "'%s'" dquot = '"%s"' noquot = "%s" wspace_plus = ' \r\n\v\t\'"' tsquot = '"""%s"""' tdquot = "'''%s'''" # Sentinel for use in getattr calls to replace hasattr MISSING = object() __version__ = '4.7.0' try: any except NameError: def any(iterable): for entry in iterable: if entry: return True return False __all__ = ( '__version__', 'DEFAULT_INDENT_TYPE', 'DEFAULT_INTERPOLATION', 'ConfigObjError', 'NestingError', 'ParseError', 'DuplicateError', 'ConfigspecError', 'ConfigObj', 'SimpleVal', 'InterpolationError', 'InterpolationLoopError', 'MissingInterpolationOption', 'RepeatSectionError', 'ReloadError', 'UnreprError', 'UnknownType', 'flatten_errors', 'get_extra_values' ) DEFAULT_INTERPOLATION = 'configparser' DEFAULT_INDENT_TYPE = ' ' MAX_INTERPOL_DEPTH = 10 OPTION_DEFAULTS = { 'interpolation': True, 'raise_errors': False, 'list_values': True, 'create_empty': False, 'file_error': False, 'configspec': None, 'stringify': True, # option may be set to one of ('', ' ', '\t') 'indent_type': None, 'encoding': None, 'default_encoding': None, 'unrepr': False, 'write_empty_values': False, } def getObj(s): global compiler if compiler is None: import compiler s = "a=" + s p = compiler.parse(s) return p.getChildren()[1].getChildren()[0].getChildren()[1] class UnknownType(Exception): pass class Builder(object): def build(self, o): m = getattr(self, 'build_' + o.__class__.__name__, None) if m is None: raise UnknownType(o.__class__.__name__) return m(o) def build_List(self, o): return map(self.build, o.getChildren()) def build_Const(self, o): return o.value def build_Dict(self, o): d = {} i = iter(map(self.build, o.getChildren())) for el in i: d[el] = i.next() return d def build_Tuple(self, o): return tuple(self.build_List(o)) def build_Name(self, o): if o.name == 'None': return None if o.name == 'True': return True if o.name == 'False': return False # An undefined Name raise UnknownType('Undefined Name') def build_Add(self, o): real, imag = map(self.build_Const, o.getChildren()) try: real = float(real) except TypeError: raise UnknownType('Add') if not isinstance(imag, complex) or imag.real != 0.0: raise UnknownType('Add') return real+imag def build_Getattr(self, o): parent = self.build(o.expr) return getattr(parent, o.attrname) def build_UnarySub(self, o): return -self.build_Const(o.getChildren()[0]) def build_UnaryAdd(self, o): return self.build_Const(o.getChildren()[0]) _builder = Builder() def unrepr(s): if not s: return s return _builder.build(getObj(s)) class ConfigObjError(SyntaxError): """ This is the base class for all errors that ConfigObj raises. It is a subclass of SyntaxError. """ def __init__(self, message='', line_number=None, line=''): self.line = line self.line_number = line_number SyntaxError.__init__(self, message) class NestingError(ConfigObjError): """ This error indicates a level of nesting that doesn't match. """ class ParseError(ConfigObjError): """ This error indicates that a line is badly written. It is neither a valid ``key = value`` line, nor a valid section marker line. """ class ReloadError(IOError): """ A 'reload' operation failed. This exception is a subclass of ``IOError``. """ def __init__(self): IOError.__init__(self, 'reload failed, filename is not set.') class DuplicateError(ConfigObjError): """ The keyword or section specified already exists. """ class ConfigspecError(ConfigObjError): """ An error occured whilst parsing a configspec. """ class InterpolationError(ConfigObjError): """Base class for the two interpolation errors.""" class InterpolationLoopError(InterpolationError): """Maximum interpolation depth exceeded in string interpolation.""" def __init__(self, option): InterpolationError.__init__( self, 'interpolation loop detected in value "%s".' % option) class RepeatSectionError(ConfigObjError): """ This error indicates additional sections in a section with a ``__many__`` (repeated) section. """ class MissingInterpolationOption(InterpolationError): """A value specified for interpolation was missing.""" def __init__(self, option): msg = 'missing option "%s" in interpolation.' % option InterpolationError.__init__(self, msg) class UnreprError(ConfigObjError): """An error parsing in unrepr mode.""" class InterpolationEngine(object): """ A helper class to help perform string interpolation. This class is an abstract base class; its descendants perform the actual work. """ # compiled regexp to use in self.interpolate() _KEYCRE = re.compile(r"%\(([^)]*)\)s") _cookie = '%' def __init__(self, section): # the Section instance that "owns" this engine self.section = section def interpolate(self, key, value): # short-cut if not self._cookie in value: return value def recursive_interpolate(key, value, section, backtrail): """The function that does the actual work. ``value``: the string we're trying to interpolate. ``section``: the section in which that string was found ``backtrail``: a dict to keep track of where we've been, to detect and prevent infinite recursion loops This is similar to a depth-first-search algorithm. """ # Have we been here already? if (key, section.name) in backtrail: # Yes - infinite loop detected raise InterpolationLoopError(key) # Place a marker on our backtrail so we won't come back here again backtrail[(key, section.name)] = 1 # Now start the actual work match = self._KEYCRE.search(value) while match: # The actual parsing of the match is implementation-dependent, # so delegate to our helper function k, v, s = self._parse_match(match) if k is None: # That's the signal that no further interpolation is needed replacement = v else: # Further interpolation may be needed to obtain final value replacement = recursive_interpolate(k, v, s, backtrail) # Replace the matched string with its final value start, end = match.span() value = ''.join((value[:start], replacement, value[end:])) new_search_start = start + len(replacement) # Pick up the next interpolation key, if any, for next time # through the while loop match = self._KEYCRE.search(value, new_search_start) # Now safe to come back here again; remove marker from backtrail del backtrail[(key, section.name)] return value # Back in interpolate(), all we have to do is kick off the recursive # function with appropriate starting values value = recursive_interpolate(key, value, self.section, {}) return value def _fetch(self, key): """Helper function to fetch values from owning section. Returns a 2-tuple: the value, and the section where it was found. """ # switch off interpolation before we try and fetch anything ! save_interp = self.section.main.interpolation self.section.main.interpolation = False # Start at section that "owns" this InterpolationEngine current_section = self.section while True: # try the current section first val = current_section.get(key) if val is not None: break # try "DEFAULT" next val = current_section.get('DEFAULT', {}).get(key) if val is not None: break # move up to parent and try again # top-level's parent is itself if current_section.parent is current_section: # reached top level, time to give up break current_section = current_section.parent # restore interpolation to previous value before returning self.section.main.interpolation = save_interp if val is None: raise MissingInterpolationOption(key) return val, current_section def _parse_match(self, match): """Implementation-dependent helper function. Will be passed a match object corresponding to the interpolation key we just found (e.g., "%(foo)s" or "$foo"). Should look up that key in the appropriate config file section (using the ``_fetch()`` helper function) and return a 3-tuple: (key, value, section) ``key`` is the name of the key we're looking for ``value`` is the value found for that key ``section`` is a reference to the section where it was found ``key`` and ``section`` should be None if no further interpolation should be performed on the resulting value (e.g., if we interpolated "$$" and returned "$"). """ raise NotImplementedError() class ConfigParserInterpolation(InterpolationEngine): """Behaves like ConfigParser.""" _cookie = '%' _KEYCRE = re.compile(r"%\(([^)]*)\)s") def _parse_match(self, match): key = match.group(1) value, section = self._fetch(key) return key, value, section class TemplateInterpolation(InterpolationEngine): """Behaves like string.Template.""" _cookie = '$' _delimiter = '$' _KEYCRE = re.compile(r""" \$(?: (?P\$) | # Two $ signs (?P[_a-z][_a-z0-9]*) | # $name format {(?P[^}]*)} # ${name} format ) """, re.IGNORECASE | re.VERBOSE) def _parse_match(self, match): # Valid name (in or out of braces): fetch value from section key = match.group('named') or match.group('braced') if key is not None: value, section = self._fetch(key) return key, value, section # Escaped delimiter (e.g., $$): return single delimiter if match.group('escaped') is not None: # Return None for key and section to indicate it's time to stop return None, self._delimiter, None # Anything else: ignore completely, just return it unchanged return None, match.group(), None interpolation_engines = { 'configparser': ConfigParserInterpolation, 'template': TemplateInterpolation, } def __newobj__(cls, *args): # Hack for pickle return cls.__new__(cls, *args) class Section(dict): """ A dictionary-like object that represents a section in a config file. It does string interpolation if the 'interpolation' attribute of the 'main' object is set to True. Interpolation is tried first from this object, then from the 'DEFAULT' section of this object, next from the parent and its 'DEFAULT' section, and so on until the main object is reached. A Section will behave like an ordered dictionary - following the order of the ``scalars`` and ``sections`` attributes. You can use this to change the order of members. Iteration follows the order: scalars, then sections. """ def __setstate__(self, state): dict.update(self, state[0]) self.__dict__.update(state[1]) def __reduce__(self): state = (dict(self), self.__dict__) return (__newobj__, (self.__class__,), state) def __init__(self, parent, depth, main, indict=None, name=None): """ * parent is the section above * depth is the depth level of this section * main is the main ConfigObj * indict is a dictionary to initialise the section with """ if indict is None: indict = {} dict.__init__(self) # used for nesting level *and* interpolation self.parent = parent # used for the interpolation attribute self.main = main # level of nesting depth of this Section self.depth = depth # purely for information self.name = name # self._initialise() # we do this explicitly so that __setitem__ is used properly # (rather than just passing to ``dict.__init__``) for entry, value in indict.iteritems(): self[entry] = value def _initialise(self): # the sequence of scalar values in this Section self.scalars = [] # the sequence of sections in this Section self.sections = [] # for comments :-) self.comments = {} self.inline_comments = {} # the configspec self.configspec = None # for defaults self.defaults = [] self.default_values = {} self.extra_values = [] self._created = False def _interpolate(self, key, value): try: # do we already have an interpolation engine? engine = self._interpolation_engine except AttributeError: # not yet: first time running _interpolate(), so pick the engine name = self.main.interpolation if name == True: # note that "if name:" would be incorrect here # backwards-compatibility: interpolation=True means use default name = DEFAULT_INTERPOLATION name = name.lower() # so that "Template", "template", etc. all work class_ = interpolation_engines.get(name, None) if class_ is None: # invalid value for self.main.interpolation self.main.interpolation = False return value else: # save reference to engine so we don't have to do this again engine = self._interpolation_engine = class_(self) # let the engine do the actual work return engine.interpolate(key, value) def __getitem__(self, key): """Fetch the item and do string interpolation.""" val = dict.__getitem__(self, key) if self.main.interpolation: if isinstance(val, basestring): return self._interpolate(key, val) if isinstance(val, list): def _check(entry): if isinstance(entry, basestring): return self._interpolate(key, entry) return entry return [_check(entry) for entry in val] return val def __setitem__(self, key, value, unrepr=False): """ Correctly set a value. Making dictionary values Section instances. (We have to special case 'Section' instances - which are also dicts) Keys must be strings. Values need only be strings (or lists of strings) if ``main.stringify`` is set. ``unrepr`` must be set when setting a value to a dictionary, without creating a new sub-section. """ if not isinstance(key, basestring): raise ValueError('The key "%s" is not a string.' % key) # add the comment if key not in self.comments: self.comments[key] = [] self.inline_comments[key] = '' # remove the entry from defaults if key in self.defaults: self.defaults.remove(key) # if isinstance(value, Section): if key not in self: self.sections.append(key) dict.__setitem__(self, key, value) elif isinstance(value, dict) and not unrepr: # First create the new depth level, # then create the section if key not in self: self.sections.append(key) new_depth = self.depth + 1 dict.__setitem__( self, key, Section( self, new_depth, self.main, indict=value, name=key)) else: if key not in self: self.scalars.append(key) if not self.main.stringify: if isinstance(value, basestring): pass elif isinstance(value, (list, tuple)): for entry in value: if not isinstance(entry, basestring): raise TypeError('Value is not a string "%s".' % entry) else: raise TypeError('Value is not a string "%s".' % value) dict.__setitem__(self, key, value) def __delitem__(self, key): """Remove items from the sequence when deleting.""" dict. __delitem__(self, key) if key in self.scalars: self.scalars.remove(key) else: self.sections.remove(key) del self.comments[key] del self.inline_comments[key] def get(self, key, default=None): """A version of ``get`` that doesn't bypass string interpolation.""" try: return self[key] except KeyError: return default def update(self, indict): """ A version of update that uses our ``__setitem__``. """ for entry in indict: self[entry] = indict[entry] def pop(self, key, *args): """ 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised' """ val = dict.pop(self, key, *args) if key in self.scalars: del self.comments[key] del self.inline_comments[key] self.scalars.remove(key) elif key in self.sections: del self.comments[key] del self.inline_comments[key] self.sections.remove(key) if self.main.interpolation and isinstance(val, basestring): return self._interpolate(key, val) return val def popitem(self): """Pops the first (key,val)""" sequence = (self.scalars + self.sections) if not sequence: raise KeyError(": 'popitem(): dictionary is empty'") key = sequence[0] val = self[key] del self[key] return key, val def clear(self): """ A version of clear that also affects scalars/sections Also clears comments and configspec. Leaves other attributes alone : depth/main/parent are not affected """ dict.clear(self) self.scalars = [] self.sections = [] self.comments = {} self.inline_comments = {} self.configspec = None self.defaults = [] self.extra_values = [] def setdefault(self, key, default=None): """A version of setdefault that sets sequence if appropriate.""" try: return self[key] except KeyError: self[key] = default return self[key] def items(self): """D.items() -> list of D's (key, value) pairs, as 2-tuples""" return zip((self.scalars + self.sections), self.values()) def keys(self): """D.keys() -> list of D's keys""" return (self.scalars + self.sections) def values(self): """D.values() -> list of D's values""" return [self[key] for key in (self.scalars + self.sections)] def iteritems(self): """D.iteritems() -> an iterator over the (key, value) items of D""" return iter(self.items()) def iterkeys(self): """D.iterkeys() -> an iterator over the keys of D""" return iter((self.scalars + self.sections)) __iter__ = iterkeys def itervalues(self): """D.itervalues() -> an iterator over the values of D""" return iter(self.values()) def __repr__(self): """x.__repr__() <==> repr(x)""" return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) for key in (self.scalars + self.sections)]) __str__ = __repr__ __str__.__doc__ = "x.__str__() <==> str(x)" # Extra methods - not in a normal dictionary def dict(self): """ Return a deepcopy of self as a dictionary. All members that are ``Section`` instances are recursively turned to ordinary dictionaries - by calling their ``dict`` method. >>> n = a.dict() >>> n == a 1 >>> n is a 0 """ newdict = {} for entry in self: this_entry = self[entry] if isinstance(this_entry, Section): this_entry = this_entry.dict() elif isinstance(this_entry, list): # create a copy rather than a reference this_entry = list(this_entry) elif isinstance(this_entry, tuple): # create a copy rather than a reference this_entry = tuple(this_entry) newdict[entry] = this_entry return newdict def merge(self, indict): """ A recursive update - useful for merging config files. >>> a = '''[section1] ... option1 = True ... [[subsection]] ... more_options = False ... # end of file'''.splitlines() >>> b = '''# File is user.ini ... [section1] ... option1 = False ... # end of file'''.splitlines() >>> c1 = ConfigObj(b) >>> c2 = ConfigObj(a) >>> c2.merge(c1) >>> c2 ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) """ for key, val in indict.items(): if (key in self and isinstance(self[key], dict) and isinstance(val, dict)): self[key].merge(val) else: self[key] = val def rename(self, oldkey, newkey): """ Change a keyname to another, without changing position in sequence. Implemented so that transformations can be made on keys, as well as on values. (used by encode and decode) Also renames comments. """ if oldkey in self.scalars: the_list = self.scalars elif oldkey in self.sections: the_list = self.sections else: raise KeyError('Key "%s" not found.' % oldkey) pos = the_list.index(oldkey) # val = self[oldkey] dict.__delitem__(self, oldkey) dict.__setitem__(self, newkey, val) the_list.remove(oldkey) the_list.insert(pos, newkey) comm = self.comments[oldkey] inline_comment = self.inline_comments[oldkey] del self.comments[oldkey] del self.inline_comments[oldkey] self.comments[newkey] = comm self.inline_comments[newkey] = inline_comment def walk(self, function, raise_errors=True, call_on_sections=False, **keywargs): """ Walk every member and call a function on the keyword and value. Return a dictionary of the return values If the function raises an exception, raise the errror unless ``raise_errors=False``, in which case set the return value to ``False``. Any unrecognised keyword arguments you pass to walk, will be pased on to the function you pass in. Note: if ``call_on_sections`` is ``True`` then - on encountering a subsection, *first* the function is called for the *whole* subsection, and then recurses into it's members. This means your function must be able to handle strings, dictionaries and lists. This allows you to change the key of subsections as well as for ordinary members. The return value when called on the whole subsection has to be discarded. See the encode and decode methods for examples, including functions. .. admonition:: caution You can use ``walk`` to transform the names of members of a section but you mustn't add or delete members. >>> config = '''[XXXXsection] ... XXXXkey = XXXXvalue'''.splitlines() >>> cfg = ConfigObj(config) >>> cfg ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) >>> def transform(section, key): ... val = section[key] ... newkey = key.replace('XXXX', 'CLIENT1') ... section.rename(key, newkey) ... if isinstance(val, (tuple, list, dict)): ... pass ... else: ... val = val.replace('XXXX', 'CLIENT1') ... section[newkey] = val >>> cfg.walk(transform, call_on_sections=True) {'CLIENT1section': {'CLIENT1key': None}} >>> cfg ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) """ out = {} # scalars first for i in range(len(self.scalars)): entry = self.scalars[i] try: val = function(self, entry, **keywargs) # bound again in case name has changed entry = self.scalars[i] out[entry] = val except Exception: if raise_errors: raise else: entry = self.scalars[i] out[entry] = False # then sections for i in range(len(self.sections)): entry = self.sections[i] if call_on_sections: try: function(self, entry, **keywargs) except Exception: if raise_errors: raise else: entry = self.sections[i] out[entry] = False # bound again in case name has changed entry = self.sections[i] # previous result is discarded out[entry] = self[entry].walk( function, raise_errors=raise_errors, call_on_sections=call_on_sections, **keywargs) return out def as_bool(self, key): """ Accepts a key as input. The corresponding value must be a string or the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to retain compatibility with Python 2.2. If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns ``True``. If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns ``False``. ``as_bool`` is not case sensitive. Any other input will raise a ``ValueError``. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_bool('a') Traceback (most recent call last): ValueError: Value "fish" is neither True nor False >>> a['b'] = 'True' >>> a.as_bool('b') 1 >>> a['b'] = 'off' >>> a.as_bool('b') 0 """ val = self[key] if val == True: return True elif val == False: return False else: try: if not isinstance(val, basestring): # TODO: Why do we raise a KeyError here? raise KeyError() else: return self.main._bools[val.lower()] except KeyError: raise ValueError('Value "%s" is neither True nor False' % val) def as_int(self, key): """ A convenience method which coerces the specified value to an integer. If the value is an invalid literal for ``int``, a ``ValueError`` will be raised. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_int('a') Traceback (most recent call last): ValueError: invalid literal for int() with base 10: 'fish' >>> a['b'] = '1' >>> a.as_int('b') 1 >>> a['b'] = '3.2' >>> a.as_int('b') Traceback (most recent call last): ValueError: invalid literal for int() with base 10: '3.2' """ return int(self[key]) def as_float(self, key): """ A convenience method which coerces the specified value to a float. If the value is an invalid literal for ``float``, a ``ValueError`` will be raised. >>> a = ConfigObj() >>> a['a'] = 'fish' >>> a.as_float('a') Traceback (most recent call last): ValueError: invalid literal for float(): fish >>> a['b'] = '1' >>> a.as_float('b') 1.0 >>> a['b'] = '3.2' >>> a.as_float('b') 3.2000000000000002 """ return float(self[key]) def as_list(self, key): """ A convenience method which fetches the specified value, guaranteeing that it is a list. >>> a = ConfigObj() >>> a['a'] = 1 >>> a.as_list('a') [1] >>> a['a'] = (1,) >>> a.as_list('a') [1] >>> a['a'] = [1] >>> a.as_list('a') [1] """ result = self[key] if isinstance(result, (tuple, list)): return list(result) return [result] def restore_default(self, key): """ Restore (and return) default value for the specified key. This method will only work for a ConfigObj that was created with a configspec and has been validated. If there is no default value for this key, ``KeyError`` is raised. """ default = self.default_values[key] dict.__setitem__(self, key, default) if key not in self.defaults: self.defaults.append(key) return default def restore_defaults(self): """ Recursively restore default values to all members that have them. This method will only work for a ConfigObj that was created with a configspec and has been validated. It doesn't delete or modify entries without default values. """ for key in self.default_values: self.restore_default(key) for section in self.sections: self[section].restore_defaults() class ConfigObj(Section): """An object to read, create, and write config files.""" _keyword = re.compile(r'''^ # line start (\s*) # indentation ( # keyword (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'"=].*?) # no quotes ) \s*=\s* # divider (.*) # value (including list values and comments) $ # line end ''', re.VERBOSE) _sectionmarker = re.compile(r'''^ (\s*) # 1: indentation ((?:\[\s*)+) # 2: section marker open ( # 3: section name open (?:"\s*\S.*?\s*")| # at least one non-space with double quotes (?:'\s*\S.*?\s*')| # at least one non-space with single quotes (?:[^'"\s].*?) # at least one non-space unquoted ) # section name close ((?:\s*\])+) # 4: section marker close \s*(\#.*)? # 5: optional comment $''', re.VERBOSE) # this regexp pulls list values out as a single string # or single values and comments # FIXME: this regex adds a '' to the end of comma terminated lists # workaround in ``_handle_value`` _valueexp = re.compile(r'''^ (?: (?: ( (?: (?: (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\#][^,\#]*?) # unquoted ) \s*,\s* # comma )* # match all list items ending in a comma (if any) ) ( (?:".*?")| # double quotes (?:'.*?')| # single quotes (?:[^'",\#\s][^,]*?)| # unquoted (?:(? 1: msg = "Parsing failed with several errors.\nFirst error %s" % info error = ConfigObjError(msg) else: error = self._errors[0] # set the errors attribute; it's a list of tuples: # (error_type, message, line_number) error.errors = self._errors # set the config attribute error.config = self raise error # delete private attributes del self._errors if configspec is None: self.configspec = None else: self._handle_configspec(configspec) def _initialise(self, options=None): if options is None: options = OPTION_DEFAULTS # initialise a few variables self.filename = None self._errors = [] self.raise_errors = options['raise_errors'] self.interpolation = options['interpolation'] self.list_values = options['list_values'] self.create_empty = options['create_empty'] self.file_error = options['file_error'] self.stringify = options['stringify'] self.indent_type = options['indent_type'] self.encoding = options['encoding'] self.default_encoding = options['default_encoding'] self.BOM = False self.newlines = None self.write_empty_values = options['write_empty_values'] self.unrepr = options['unrepr'] self.initial_comment = [] self.final_comment = [] self.configspec = None if self._inspec: self.list_values = False # Clear section attributes as well Section._initialise(self) def __repr__(self): return ('ConfigObj({%s})' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) for key in (self.scalars + self.sections)])) def _handle_bom(self, infile): """ Handle any BOM, and decode if necessary. If an encoding is specified, that *must* be used - but the BOM should still be removed (and the BOM attribute set). (If the encoding is wrongly specified, then a BOM for an alternative encoding won't be discovered or removed.) If an encoding is not specified, UTF8 or UTF16 BOM will be detected and removed. The BOM attribute will be set. UTF16 will be decoded to unicode. NOTE: This method must not be called with an empty ``infile``. Specifying the *wrong* encoding is likely to cause a ``UnicodeDecodeError``. ``infile`` must always be returned as a list of lines, but may be passed in as a single string. """ if ((self.encoding is not None) and (self.encoding.lower() not in BOM_LIST)): # No need to check for a BOM # the encoding specified doesn't have one # just decode return self._decode(infile, self.encoding) if isinstance(infile, (list, tuple)): line = infile[0] else: line = infile if self.encoding is not None: # encoding explicitly supplied # And it could have an associated BOM # TODO: if encoding is just UTF16 - we ought to check for both # TODO: big endian and little endian versions. enc = BOM_LIST[self.encoding.lower()] if enc == 'utf_16': # For UTF16 we try big endian and little endian for BOM, (encoding, final_encoding) in BOMS.items(): if not final_encoding: # skip UTF8 continue if infile.startswith(BOM): ### BOM discovered ##self.BOM = True # Don't need to remove BOM return self._decode(infile, encoding) # If we get this far, will *probably* raise a DecodeError # As it doesn't appear to start with a BOM return self._decode(infile, self.encoding) # Must be UTF8 BOM = BOM_SET[enc] if not line.startswith(BOM): return self._decode(infile, self.encoding) newline = line[len(BOM):] # BOM removed if isinstance(infile, (list, tuple)): infile[0] = newline else: infile = newline self.BOM = True return self._decode(infile, self.encoding) # No encoding specified - so we need to check for UTF8/UTF16 for BOM, (encoding, final_encoding) in BOMS.items(): if not line.startswith(BOM): continue else: # BOM discovered self.encoding = final_encoding if not final_encoding: self.BOM = True # UTF8 # remove BOM newline = line[len(BOM):] if isinstance(infile, (list, tuple)): infile[0] = newline else: infile = newline # UTF8 - don't decode if isinstance(infile, basestring): return infile.splitlines(True) else: return infile # UTF16 - have to decode return self._decode(infile, encoding) # No BOM discovered and no encoding specified, just return if isinstance(infile, basestring): # infile read from a file will be a single string return infile.splitlines(True) return infile def _a_to_u(self, aString): """Decode ASCII strings to unicode if a self.encoding is specified.""" if self.encoding: return aString.decode('ascii') else: return aString def _decode(self, infile, encoding): """ Decode infile to unicode. Using the specified encoding. if is a string, it also needs converting to a list. """ if isinstance(infile, basestring): # can't be unicode # NOTE: Could raise a ``UnicodeDecodeError`` return infile.decode(encoding).splitlines(True) for i, line in enumerate(infile): if not isinstance(line, unicode): # NOTE: The isinstance test here handles mixed lists of unicode/string # NOTE: But the decode will break on any non-string values # NOTE: Or could raise a ``UnicodeDecodeError`` infile[i] = line.decode(encoding) return infile def _decode_element(self, line): """Decode element to unicode if necessary.""" if not self.encoding: return line if isinstance(line, str) and self.default_encoding: return line.decode(self.default_encoding) return line def _str(self, value): """ Used by ``stringify`` within validate, to turn non-string values into strings. """ if not isinstance(value, basestring): return str(value) else: return value def _parse(self, infile): """Actually parse the config file.""" temp_list_values = self.list_values if self.unrepr: self.list_values = False comment_list = [] done_start = False this_section = self maxline = len(infile) - 1 cur_index = -1 reset_comment = False while cur_index < maxline: if reset_comment: comment_list = [] cur_index += 1 line = infile[cur_index] sline = line.strip() # do we have anything on the line ? if not sline or sline.startswith('#'): reset_comment = False comment_list.append(line) continue if not done_start: # preserve initial comment self.initial_comment = comment_list comment_list = [] done_start = True reset_comment = True # first we check if it's a section marker mat = self._sectionmarker.match(line) if mat is not None: # is a section line (indent, sect_open, sect_name, sect_close, comment) = mat.groups() if indent and (self.indent_type is None): self.indent_type = indent cur_depth = sect_open.count('[') if cur_depth != sect_close.count(']'): self._handle_error("Cannot compute the section depth at line %s.", NestingError, infile, cur_index) continue if cur_depth < this_section.depth: # the new section is dropping back to a previous level try: parent = self._match_depth(this_section, cur_depth).parent except SyntaxError: self._handle_error("Cannot compute nesting level at line %s.", NestingError, infile, cur_index) continue elif cur_depth == this_section.depth: # the new section is a sibling of the current section parent = this_section.parent elif cur_depth == this_section.depth + 1: # the new section is a child the current section parent = this_section else: self._handle_error("Section too nested at line %s.", NestingError, infile, cur_index) sect_name = self._unquote(sect_name) if sect_name in parent: self._handle_error('Duplicate section name at line %s.', DuplicateError, infile, cur_index) continue # create the new section this_section = Section( parent, cur_depth, self, name=sect_name) parent[sect_name] = this_section parent.inline_comments[sect_name] = comment parent.comments[sect_name] = comment_list continue # # it's not a section marker, # so it should be a valid ``key = value`` line mat = self._keyword.match(line) if mat is None: # it neither matched as a keyword # or a section marker self._handle_error( 'Invalid line at line "%s".', ParseError, infile, cur_index) else: # is a keyword value # value will include any inline comment (indent, key, value) = mat.groups() if indent and (self.indent_type is None): self.indent_type = indent # check for a multiline value if value[:3] in ['"""', "'''"]: try: value, comment, cur_index = self._multiline( value, infile, cur_index, maxline) except SyntaxError: self._handle_error( 'Parse error in value at line %s.', ParseError, infile, cur_index) continue else: if self.unrepr: comment = '' try: value = unrepr(value) except Exception, e: if type(e) == UnknownType: msg = 'Unknown name or type in value at line %s.' else: msg = 'Parse error in value at line %s.' self._handle_error(msg, UnreprError, infile, cur_index) continue else: if self.unrepr: comment = '' try: value = unrepr(value) except Exception, e: if isinstance(e, UnknownType): msg = 'Unknown name or type in value at line %s.' else: msg = 'Parse error in value at line %s.' self._handle_error(msg, UnreprError, infile, cur_index) continue else: # extract comment and lists try: (value, comment) = self._handle_value(value) except SyntaxError: self._handle_error( 'Parse error in value at line %s.', ParseError, infile, cur_index) continue # key = self._unquote(key) if key in this_section: self._handle_error( 'Duplicate keyword name at line %s.', DuplicateError, infile, cur_index) continue # add the key. # we set unrepr because if we have got this far we will never # be creating a new section this_section.__setitem__(key, value, unrepr=True) this_section.inline_comments[key] = comment this_section.comments[key] = comment_list continue # if self.indent_type is None: # no indentation used, set the type accordingly self.indent_type = '' # preserve the final comment if not self and not self.initial_comment: self.initial_comment = comment_list elif not reset_comment: self.final_comment = comment_list self.list_values = temp_list_values def _match_depth(self, sect, depth): """ Given a section and a depth level, walk back through the sections parents to see if the depth level matches a previous section. Return a reference to the right section, or raise a SyntaxError. """ while depth < sect.depth: if sect is sect.parent: # we've reached the top level already raise SyntaxError() sect = sect.parent if sect.depth == depth: return sect # shouldn't get here raise SyntaxError() def _handle_error(self, text, ErrorClass, infile, cur_index): """ Handle an error according to the error settings. Either raise the error or store it. The error will have occured at ``cur_index`` """ line = infile[cur_index] cur_index += 1 message = text % cur_index error = ErrorClass(message, cur_index, line) if self.raise_errors: # raise the error - parsing stops here raise error # store the error # reraise when parsing has finished self._errors.append(error) def _unquote(self, value): """Return an unquoted version of a value""" if not value: # should only happen during parsing of lists raise SyntaxError if (value[0] == value[-1]) and (value[0] in ('"', "'")): value = value[1:-1] return value def _quote(self, value, multiline=True): """ Return a safely quoted version of a value. Raise a ConfigObjError if the value cannot be safely quoted. If multiline is ``True`` (default) then use triple quotes if necessary. * Don't quote values that don't need it. * Recursively quote members of a list and return a comma joined list. * Multiline is ``False`` for lists. * Obey list syntax for empty and single member lists. If ``list_values=False`` then the value is only quoted if it contains a ``\\n`` (is multiline) or '#'. If ``write_empty_values`` is set, and the value is an empty string, it won't be quoted. """ if multiline and self.write_empty_values and value == '': # Only if multiline is set, so that it is used for values not # keys, and not values that are part of a list return '' if multiline and isinstance(value, (list, tuple)): if not value: return ',' elif len(value) == 1: return self._quote(value[0], multiline=False) + ',' return ', '.join([self._quote(val, multiline=False) for val in value]) if not isinstance(value, basestring): if self.stringify: value = str(value) else: raise TypeError('Value "%s" is not a string.' % value) if not value: return '""' no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote if check_for_single: if not self.list_values: # we don't quote if ``list_values=False`` quot = noquot # for normal values either single or double quotes will do elif '\n' in value: # will only happen if multiline is off - e.g. '\n' in key raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) elif ((value[0] not in wspace_plus) and (value[-1] not in wspace_plus) and (',' not in value)): quot = noquot else: quot = self._get_single_quote(value) else: # if value has '\n' or "'" *and* '"', it will need triple quotes quot = self._get_triple_quote(value) if quot == noquot and '#' in value and self.list_values: quot = self._get_single_quote(value) return quot % value def _get_single_quote(self, value): if ("'" in value) and ('"' in value): raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) elif '"' in value: quot = squot else: quot = dquot return quot def _get_triple_quote(self, value): if (value.find('"""') != -1) and (value.find("'''") != -1): raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) if value.find('"""') == -1: quot = tdquot else: quot = tsquot return quot def _handle_value(self, value): """ Given a value string, unquote, remove comment, handle lists. (including empty and single member lists) """ if self._inspec: # Parsing a configspec so don't handle comments return (value, '') # do we look for lists in values ? if not self.list_values: mat = self._nolistvalue.match(value) if mat is None: raise SyntaxError() # NOTE: we don't unquote here return mat.groups() # mat = self._valueexp.match(value) if mat is None: # the value is badly constructed, probably badly quoted, # or an invalid list raise SyntaxError() (list_values, single, empty_list, comment) = mat.groups() if (list_values == '') and (single is None): # change this if you want to accept empty values raise SyntaxError() # NOTE: note there is no error handling from here if the regex # is wrong: then incorrect values will slip through if empty_list is not None: # the single comma - meaning an empty list return ([], comment) if single is not None: # handle empty values if list_values and not single: # FIXME: the '' is a workaround because our regex now matches # '' at the end of a list if it has a trailing comma single = None else: single = single or '""' single = self._unquote(single) if list_values == '': # not a list value return (single, comment) the_list = self._listvalueexp.findall(list_values) the_list = [self._unquote(val) for val in the_list] if single is not None: the_list += [single] return (the_list, comment) def _multiline(self, value, infile, cur_index, maxline): """Extract the value, where we are in a multiline situation.""" quot = value[:3] newvalue = value[3:] single_line = self._triple_quote[quot][0] multi_line = self._triple_quote[quot][1] mat = single_line.match(value) if mat is not None: retval = list(mat.groups()) retval.append(cur_index) return retval elif newvalue.find(quot) != -1: # somehow the triple quote is missing raise SyntaxError() # while cur_index < maxline: cur_index += 1 newvalue += '\n' line = infile[cur_index] if line.find(quot) == -1: newvalue += line else: # end of multiline, process it break else: # we've got to the end of the config, oops... raise SyntaxError() mat = multi_line.match(line) if mat is None: # a badly formed line raise SyntaxError() (value, comment) = mat.groups() return (newvalue + value, comment, cur_index) def _handle_configspec(self, configspec): """Parse the configspec.""" # FIXME: Should we check that the configspec was created with the # correct settings ? (i.e. ``list_values=False``) if not isinstance(configspec, ConfigObj): try: configspec = ConfigObj(configspec, raise_errors=True, file_error=True, _inspec=True) except ConfigObjError, e: # FIXME: Should these errors have a reference # to the already parsed ConfigObj ? raise ConfigspecError('Parsing configspec failed: %s' % e) except IOError, e: raise IOError('Reading configspec failed: %s' % e) self.configspec = configspec def _set_configspec(self, section, copy): """ Called by validate. Handles setting the configspec on subsections including sections to be validated by __many__ """ configspec = section.configspec many = configspec.get('__many__') if isinstance(many, dict): for entry in section.sections: if entry not in configspec: section[entry].configspec = many for entry in configspec.sections: if entry == '__many__': continue if entry not in section: section[entry] = {} section[entry]._created = True if copy: # copy comments section.comments[entry] = configspec.comments.get(entry, []) section.inline_comments[entry] = configspec.inline_comments.get(entry, '') # Could be a scalar when we expect a section if isinstance(section[entry], Section): section[entry].configspec = configspec[entry] def _write_line(self, indent_string, entry, this_entry, comment): """Write an individual line, for the write method""" # NOTE: the calls to self._quote here handles non-StringType values. if not self.unrepr: val = self._decode_element(self._quote(this_entry)) else: val = repr(this_entry) return '%s%s%s%s%s' % (indent_string, self._decode_element(self._quote(entry, multiline=False)), self._a_to_u(' = '), val, self._decode_element(comment)) def _write_marker(self, indent_string, depth, entry, comment): """Write a section marker line""" return '%s%s%s%s%s' % (indent_string, self._a_to_u('[' * depth), self._quote(self._decode_element(entry), multiline=False), self._a_to_u(']' * depth), self._decode_element(comment)) def _handle_comment(self, comment): """Deal with a comment.""" if not comment: return '' start = self.indent_type if not comment.startswith('#'): start += self._a_to_u(' # ') return (start + comment) # Public methods def write(self, outfile=None, section=None): """ Write the current ConfigObj as a file tekNico: FIXME: use StringIO instead of real files >>> filename = a.filename >>> a.filename = 'test.ini' >>> a.write() >>> a.filename = filename >>> a == ConfigObj('test.ini', raise_errors=True) 1 """ if self.indent_type is None: # this can be true if initialised from a dictionary self.indent_type = DEFAULT_INDENT_TYPE out = [] cs = self._a_to_u('#') csp = self._a_to_u('# ') if section is None: int_val = self.interpolation self.interpolation = False section = self for line in self.initial_comment: line = self._decode_element(line) stripped_line = line.strip() if stripped_line and not stripped_line.startswith(cs): line = csp + line out.append(line) indent_string = self.indent_type * section.depth for entry in (section.scalars + section.sections): if entry in section.defaults: # don't write out default values continue for comment_line in section.comments[entry]: comment_line = self._decode_element(comment_line.lstrip()) if comment_line and not comment_line.startswith(cs): comment_line = csp + comment_line out.append(indent_string + comment_line) this_entry = section[entry] comment = self._handle_comment(section.inline_comments[entry]) if isinstance(this_entry, dict): # a section out.append(self._write_marker( indent_string, this_entry.depth, entry, comment)) out.extend(self.write(section=this_entry)) else: out.append(self._write_line( indent_string, entry, this_entry, comment)) if section is self: for line in self.final_comment: line = self._decode_element(line) stripped_line = line.strip() if stripped_line and not stripped_line.startswith(cs): line = csp + line out.append(line) self.interpolation = int_val if section is not self: return out if (self.filename is None) and (outfile is None): # output a list of lines # might need to encode # NOTE: This will *screw* UTF16, each line will start with the BOM if self.encoding: out = [l.encode(self.encoding) for l in out] if (self.BOM and ((self.encoding is None) or (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): # Add the UTF8 BOM if not out: out.append('') out[0] = BOM_UTF8 + out[0] return out # Turn the list to a string, joined with correct newlines newline = self.newlines or os.linesep output = self._a_to_u(newline).join(out) if self.encoding: output = output.encode(self.encoding) if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): # Add the UTF8 BOM output = BOM_UTF8 + output if not output.endswith(newline): output += newline if outfile is not None: outfile.write(output) else: h = open(self.filename, 'wb') h.write(output) h.close() def validate(self, validator, preserve_errors=False, copy=False, section=None): """ Test the ConfigObj against a configspec. It uses the ``validator`` object from *validate.py*. To run ``validate`` on the current ConfigObj, call: :: test = config.validate(validator) (Normally having previously passed in the configspec when the ConfigObj was created - you can dynamically assign a dictionary of checks to the ``configspec`` attribute of a section though). It returns ``True`` if everything passes, or a dictionary of pass/fails (True/False). If every member of a subsection passes, it will just have the value ``True``. (It also returns ``False`` if all members fail). In addition, it converts the values from strings to their native types if their checks pass (and ``stringify`` is set). If ``preserve_errors`` is ``True`` (``False`` is default) then instead of a marking a fail with a ``False``, it will preserve the actual exception object. This can contain info about the reason for failure. For example the ``VdtValueTooSmallError`` indicates that the value supplied was too small. If a value (or section) is missing it will still be marked as ``False``. You must have the validate module to use ``preserve_errors=True``. You can then use the ``flatten_errors`` function to turn your nested results dictionary into a flattened list of failures - useful for displaying meaningful error messages. """ if section is None: if self.configspec is None: raise ValueError('No configspec supplied.') if preserve_errors: # We do this once to remove a top level dependency on the validate module # Which makes importing configobj faster from validate import VdtMissingValue self._vdtMissingValue = VdtMissingValue section = self if copy: section.initial_comment = section.configspec.initial_comment section.final_comment = section.configspec.final_comment section.encoding = section.configspec.encoding section.BOM = section.configspec.BOM section.newlines = section.configspec.newlines section.indent_type = section.configspec.indent_type # # section.default_values.clear() #?? configspec = section.configspec self._set_configspec(section, copy) def validate_entry(entry, spec, val, missing, ret_true, ret_false): section.default_values.pop(entry, None) try: section.default_values[entry] = validator.get_default_value(configspec[entry]) except (KeyError, AttributeError, validator.baseErrorClass): # No default, bad default or validator has no 'get_default_value' # (e.g. SimpleVal) pass try: check = validator.check(spec, val, missing=missing ) except validator.baseErrorClass, e: if not preserve_errors or isinstance(e, self._vdtMissingValue): out[entry] = False else: # preserve the error out[entry] = e ret_false = False ret_true = False else: ret_false = False out[entry] = True if self.stringify or missing: # if we are doing type conversion # or the value is a supplied default if not self.stringify: if isinstance(check, (list, tuple)): # preserve lists check = [self._str(item) for item in check] elif missing and check is None: # convert the None from a default to a '' check = '' else: check = self._str(check) if (check != val) or missing: section[entry] = check if not copy and missing and entry not in section.defaults: section.defaults.append(entry) return ret_true, ret_false # out = {} ret_true = True ret_false = True unvalidated = [k for k in section.scalars if k not in configspec] incorrect_sections = [k for k in configspec.sections if k in section.scalars] incorrect_scalars = [k for k in configspec.scalars if k in section.sections] for entry in configspec.scalars: if entry in ('__many__', '___many___'): # reserved names continue if (not entry in section.scalars) or (entry in section.defaults): # missing entries # or entries from defaults missing = True val = None if copy and entry not in section.scalars: # copy comments section.comments[entry] = ( configspec.comments.get(entry, [])) section.inline_comments[entry] = ( configspec.inline_comments.get(entry, '')) # else: missing = False val = section[entry] ret_true, ret_false = validate_entry(entry, configspec[entry], val, missing, ret_true, ret_false) many = None if '__many__' in configspec.scalars: many = configspec['__many__'] elif '___many___' in configspec.scalars: many = configspec['___many___'] if many is not None: for entry in unvalidated: val = section[entry] ret_true, ret_false = validate_entry(entry, many, val, False, ret_true, ret_false) unvalidated = [] for entry in incorrect_scalars: ret_true = False if not preserve_errors: out[entry] = False else: ret_false = False msg = 'Value %r was provided as a section' % entry out[entry] = validator.baseErrorClass(msg) for entry in incorrect_sections: ret_true = False if not preserve_errors: out[entry] = False else: ret_false = False msg = 'Section %r was provided as a single value' % entry out[entry] = validator.baseErrorClass(msg) # Missing sections will have been created as empty ones when the # configspec was read. for entry in section.sections: # FIXME: this means DEFAULT is not copied in copy mode if section is self and entry == 'DEFAULT': continue if section[entry].configspec is None: unvalidated.append(entry) continue if copy: section.comments[entry] = configspec.comments.get(entry, []) section.inline_comments[entry] = configspec.inline_comments.get(entry, '') check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) out[entry] = check if check == False: ret_true = False elif check == True: ret_false = False else: ret_true = False section.extra_values = unvalidated if preserve_errors and not section._created: # If the section wasn't created (i.e. it wasn't missing) # then we can't return False, we need to preserve errors ret_false = False # if ret_false and preserve_errors and out: # If we are preserving errors, but all # the failures are from missing sections / values # then we can return False. Otherwise there is a # real failure that we need to preserve. ret_false = not any(out.values()) if ret_true: return True elif ret_false: return False return out def reset(self): """Clear ConfigObj instance and restore to 'freshly created' state.""" self.clear() self._initialise() # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) # requires an empty dictionary self.configspec = None # Just to be sure ;-) self._original_configspec = None def reload(self): """ Reload a ConfigObj from file. This method raises a ``ReloadError`` if the ConfigObj doesn't have a filename attribute pointing to a file. """ if not isinstance(self.filename, basestring): raise ReloadError() filename = self.filename current_options = {} for entry in OPTION_DEFAULTS: if entry == 'configspec': continue current_options[entry] = getattr(self, entry) configspec = self._original_configspec current_options['configspec'] = configspec self.clear() self._initialise(current_options) self._load(filename, configspec) class SimpleVal(object): """ A simple validator. Can be used to check that all members expected are present. To use it, provide a configspec with all your members in (the value given will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` method of your ``ConfigObj``. ``validate`` will return ``True`` if all members are present, or a dictionary with True/False meaning present/missing. (Whole missing sections will be replaced with ``False``) """ def __init__(self): self.baseErrorClass = ConfigObjError def check(self, check, member, missing=False): """A dummy check method, always returns the value unchanged.""" if missing: raise self.baseErrorClass() return member def flatten_errors(cfg, res, levels=None, results=None): """ An example function that will turn a nested dictionary of results (as returned by ``ConfigObj.validate``) into a flat list. ``cfg`` is the ConfigObj instance being checked, ``res`` is the results dictionary returned by ``validate``. (This is a recursive function, so you shouldn't use the ``levels`` or ``results`` arguments - they are used by the function.) Returns a list of keys that failed. Each member of the list is a tuple:: ([list of sections...], key, result) If ``validate`` was called with ``preserve_errors=False`` (the default) then ``result`` will always be ``False``. *list of sections* is a flattened list of sections that the key was found in. If the section was missing (or a section was expected and a scalar provided - or vice-versa) then key will be ``None``. If the value (or section) was missing then ``result`` will be ``False``. If ``validate`` was called with ``preserve_errors=True`` and a value was present, but failed the check, then ``result`` will be the exception object returned. You can use this as a string that describes the failure. For example *The value "3" is of the wrong type*. """ if levels is None: # first time called levels = [] results = [] if res == True: return results if res == False or isinstance(res, Exception): results.append((levels[:], None, res)) if levels: levels.pop() return results for (key, val) in res.items(): if val == True: continue if isinstance(cfg.get(key), dict): # Go down one level levels.append(key) flatten_errors(cfg[key], val, levels, results) continue results.append((levels[:], key, val)) # # Go up one level if levels: levels.pop() # return results def get_extra_values(conf, _prepend=()): """ Find all the values and sections not in the configspec from a validated ConfigObj. ``get_extra_values`` returns a list of tuples where each tuple represents either an extra section, or an extra value. The tuples contain two values, a tuple representing the section the value is in and the name of the extra values. For extra values in the top level section the first member will be an empty tuple. For values in the 'foo' section the first member will be ``('foo',)``. For members in the 'bar' subsection of the 'foo' section the first member will be ``('foo', 'bar')``. NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't been validated it will return an empty list. """ out = [] out.extend((_prepend, name) for name in conf.extra_values) for name in conf.sections: if name not in conf.extra_values: out.extend(get_extra_values(conf[name], _prepend + (name,))) return out """*A programming language is a medium of expression.* - Paul Graham""" openxenmanager/oxcSERVER_vm_snapshot.py0000644000175000017500000000574711636446664017033 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERvmsnapshot: def take_snapshot(self, ref, snapname): res = self.connection.Async.VM.snapshot(self.session_uuid, ref, snapname) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def revert_to_snapshot(self, ref, snapref): res = self.connection.Async.VM.revert(self.session_uuid, snapref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def delete_snapshot(self, ref, ref_vm): for vbd in self.all_vms[ref]['VBDs']: if self.all_vbd[vbd]['VDI'] != "OpaqueRef:NULL": res = self.connection.VDI.destroy(self.session_uuid, self.all_vbd[vbd]['VDI']) if "Value" in res: self.track_tasks[res['Value']] = ref_vm self.track_tasks[res['Value']] = ref else: print res for vbd in self.all_vms[ref]['VBDs']: res = self.connection.VBD.destroy(self.session_uuid, vbd) if "Value" in res: self.track_tasks[res['Value']] = ref_vm self.track_tasks[res['Value']] = ref else: print res res = self.connection.VM.destroy(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref_vm self.track_tasks[res['Value']] = ref else: print res def create_template_from_snap(self, ref, name): res = self.connection.Async.VM.clone(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res openxenmanager/oxcSERVER_storage.py0000644000175000017500000006662611636446664016141 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header import xml.sax.saxutils as saxutils class oxcSERVERstorage: stg_ref = None stg_uuid = None def fill_hw_hba(self, ref, list): #SR.probeOpaqueRef:c9ea013c-cbce-0e85-6863-66d8e7b66ea7OpaqueRef:5c0a69d1-7719-946b-7f3c-683a7058338dlvmohba list.clear() res = self.connection.SR.probe(self.session_uuid, ref, {}, "lvmohba", {}) if len(res['ErrorDescription']) > 2: result = res['ErrorDescription'][3] dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("BlockDevice") disks = {} for node in nodes: size = self.convert_bytes(node.getElementsByTagName("size")[0].childNodes[0].data.strip()) serial = node.getElementsByTagName("serial")[0].childNodes[0].data.strip() scsiid = node.getElementsByTagName("SCSIid")[0].childNodes[0].data.strip() adapter = node.getElementsByTagName("adapter")[0].childNodes[0].data.strip() channel = node.getElementsByTagName("channel")[0].childNodes[0].data.strip() id = node.getElementsByTagName("id")[0].childNodes[0].data.strip() lun = node.getElementsByTagName("lun")[0].childNodes[0].data.strip() vendor = node.getElementsByTagName("vendor")[0].childNodes[0].data.strip() path = node.getElementsByTagName("path")[0].childNodes[0].data.strip() if vendor not in disks: disks[vendor] = [] disks[vendor].append([" %s %s %s %s:%s:%s:%s" % (size, serial, scsiid, adapter, channel, id, lun),scsiid,path]) for ref in disks.keys(): list.append(["" + ref + "", False, "", ""]) for lun in disks[ref]: list.append([lun[0], True, lun[1], lun[2]]) return 0 else: self.wine.show_error_dlg("No LUNs were found. Please verify your hardware configuration") return 1 def rescan_isos(self, ref): res = self.connection.Async.SR.scan(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def detach_storage(self, ref): for pbd in self.all_storage[ref]['PBDs']: res = self.connection.Async.PBD.unplug(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res if "Value" in res: value = res["Value"] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] res = self.connection.PBD.destroy(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def forget_storage(self, ref): if self.all_storage[ref]['allowed_operations'].count("unplug"): for pbd in self.all_storage[ref]['PBDs']: res = self.connection.Async.PBD.unplug(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref value = res["Value"] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] res = self.connection.Async.PBD.destroy(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref value = res["Value"] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] else: print res else: print res res = self.connection.Async.SR.forget(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def delete_vdi(self, ref_vdi, ref_vm): for ref_vbd in self.all_vdi[ref_vdi]['VBDs']: res = self.connection.VBD.destroy(self.session_uuid, ref_vbd) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res res = self.connection.VDI.destroy(self.session_uuid, ref_vdi) if "Value" in res: self.track_tasks[res['Value']] = ref_vm else: print res def reattach_nfs_iso(self, sr, name, share, options): # FIXME ref = self.all_hosts.keys()[0] pbd = { "uuid" : "", "host" : ref, "SR" : sr, "device_config" : { "location" : share, "options": options }, "currentyle_attached" : False, "other_config" : {} } self.connection.SR.set_name_label(self.session_uuid, sr, name) self.connection.SR.set_name_description(self.session_uuid, sr, "NFS ISO Library [%s]" % (share)) res = self.connection.Async.PBD.create(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') res = self.connection.Async.PBD.plug(self.session_uuid, result) value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["status"] == "success": return 0 else: self.wine.show_error_dlg(str(task["error_info"])) return 1 else: print res def create_nfs_iso(self, ref, name, share, options): sr = { "location" : share, "options" : options } value = self.connection.SR.create(self.session_uuid, ref, sr, "0", name, "NFS ISO Library [%s]" % (share), "iso", "iso", True, {}) if "ErrorDescription" in value: self.wine.show_error_dlg(value["ErrorDescription"][2]) return 1 else: return 0 def reattach_cifs_iso(self, sr, name, share, options, user="", password=""): ref = self.all_hosts.keys()[0] pbd = { "uuid" : "", "host" : ref, "SR" : sr, "device_config" : { "location" : share, "type": "cifs", "options": options }, "currentyle_attached" : False, "other_config" : {} } self.connection.SR.set_name_label(self.session_uuid, sr, name) self.connection.SR.set_name_description(self.session_uuid, sr, "CIFS ISO Library [%s]" % (share)) res = self.connection.Async.PBD.create(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = ref value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') res = self.connection.Async.PBD.plug(self.session_uuid, result) value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["status"] == "success": return 0 else: self.wine.show_error_dlg(str(task["error_info"])) return 1 else: print res def create_cifs_iso(self, ref, name, share, options, user="", password=""): sr = { "location" : share, "type" : "cifs", "options" : options, "username" : user, "cifspassword" : password, } value = self.connection.SR.create(self.session_uuid, ref, sr, "0", name, "CIFS ISO Library [%s]" % (share), "iso", "iso", True, {}) if "ErrorDescription" in value: self.wine.show_error_dlg(value["ErrorDescription"][2]) return 1 else: return 0 def create_nfs_vhd(self, ref, name, host, path, options, create=None): sr = { "serverpath" : path, "server" : host, "options" : options } res = self.connection.SR.create(self.session_uuid, ref, sr, str(0), name, "NF SR [%s:%s]" % (host, path), "nfs", "", True, {}) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def create_aoe(self, ref, name, path, create=None): sr = { "device" : path, } res = self.connection.SR.create(self.session_uuid, ref, sr, str(0), name, "AoE SR [%s]" % (path), "lvm", "", True, {}) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def reattach_aoe(self, ref, name, path, create, uuid): sr = self.connection.SR.get_by_uuid(self.session_uuid, uuid) sr = self.connection.SR.introduce(self.session_uuid, uuid, name, "AOE SR [%s]" % (path), "lvm", "", True, {})['Value'] pbd = { "uuid" : "", "host" : ref, "SR" : sr, "device_config" : { "device" : path, }, "currently_attached" : False, "other_config" : {} } ref = self.connection.PBD.create(self.session_uuid, pbd)['Value'] self.connection.PBD.plug(self.session_uuid, ref) def reattach_nfs_vhd(self, ref, name, host, path, options, create, uuid): sr = self.connection.SR.get_by_uuid(self.session_uuid, uuid) sr = self.connection.SR.introduce(self.session_uuid, uuid, name, "NFS SR [%s:%s]" % (host, path), "nfs", "", True, {})['Value'] pbd = { "uuid" : "", "host" : ref, "SR" : sr, "device_config" : { "serverpath" : path, "server" : host, "options": options }, "currently_attached" : False, "other_config" : {} } ref = self.connection.PBD.create(self.session_uuid, pbd)['Value'] self.connection.PBD.plug(self.session_uuid, ref) def format_hardware_hba(self, ref, uuid, name, path): sr = { "SCSIid" : uuid, } res = self.connection.SR.create(self.session_uuid, ref, sr, "0", name, "Hardware HBA SR [%s]" % (path), "lvmohba", "", False, {}) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] print self.connection.SR.set_other_config(self.session_uuid, res['Value'], {"auto-scan": "false"}) else: print res def reattach_and_introduce_hardware_hba(self, ref, uuid, name, path): res = self.connection.SR.introduce(self.session_uuid, self.stg_uuid, name, "Hardware HBA SR [%s]" % (path), "lvmohba", "", False, {}) pbd = { "uuid" : "", "host" : ref, "SR" : res['Value'], "device_config" : { "SCSIid" : uuid, }, "currentyle_attached" : False, "other_config" : {} } res = self.connection.Async.PBD.create(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') res = self.connection.Async.PBD.plug(self.session_uuid, result) value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["status"] == "success": return 0 else: self.wine.show_error_dlg(str(task["error_info"])) return 1 else: print res def reattach_hardware_hba(self, ref, uuid, name, path): ref = self.all_hosts.keys()[0] pbd = { "uuid" : "", "host" : ref, "SR" : self.stg_ref, "device_config" : { "SCSIid" : uuid, }, "currentyle_attached" : False, "other_config" : {} } self.connection.SR.set_name_label(self.session_uuid, self.stg_ref, name) self.connection.SR.set_name_description(self.session_uuid, self.stg_ref, "Hardware HBA SR [%s]" % (path)) res = self.connection.Async.PBD.create(self.session_uuid, pbd) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') res = self.connection.Async.PBD.plug(self.session_uuid, result) value = res['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["status"] == "success": return 0 else: self.wine.show_error_dlg(str(task["error_info"])) return 1 else: print res pass """ sr = { "SCSIid" : uuid, } res = self.connection.SR.create(self.session_uuid, ref, sr, "0", name, "Hardware HBA SR [IBM - %s]" % (path), "lvmohba", "", False, {}) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] print self.connection.SR.set_other_config(self.session_uuid, res['Value'], {"auto-scan": "false"}) else: print res """ def check_hardware_hba(self, ref, uuid, text): result = self.connection.SR.probe(self.session_uuid, ref, {"SCSIid" : uuid }, "lvmohba", {})['Value'] dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("UUID") if len(nodes): reattach = True self.stg_uuid = nodes[0].childNodes[0].data.strip() for storage_ref in self.all_storage.keys(): storage = self.all_storage[storage_ref] if storage["uuid"] == self.stg_uuid: self.stg_ref = storage_ref if len(storage['PBDs']): reattach = False if reattach: if self.stg_ref: return [2, self.all_storage[self.stg_ref]['name_label'], self.all_hosts[ref]['name_label']] else: return [3, text, self.all_hosts[ref]['name_label']] else: return [1, self.all_storage[self.stg_ref]['name_label'], self.all_hosts[ref]['name_label']] else: return [0, None, None] def check_iscsi(self, ref, name, host, port, scsiid, targetiqn, user, password): sr = { "port" : port, "target" : host, "SCSIid" : scsiid, "targetIQN" : targetiqn } if user: sr["chapuser"] = user if password: sr["chappassword"] = password value = self.connection.Async.SR.probe(self.session_uuid, ref, sr, "lvmoiscsi", {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') print result dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("UUID") if len(nodes): return nodes[0].childNodes[0].data.strip() else: return None #ref = self.connection.SR.create(self.session_uuid, ref, sr, "0", name, "iSCSI SR [%s (%s)]" % (host, targetiqn), "lvmoiscsi", "", True, {}) #print ref def create_iscsi(self, ref, name, host, port, scsiid, targetiqn, user, password): sr = { "port" : port, "target" : host, "SCSIid" : scsiid, "targetIQN" : targetiqn } if user: sr["chapuser"] = user if password: sr["chappassword"] = password res = self.connection.Async.SR.create(self.session_uuid, ref, sr, "0", name, "iSCSI SR [%s (%s)]" % (host, targetiqn), "lvmoiscsi", "", True, {}) def reattach_iscsi(self, ref, name, host, port, scsiid, targetiqn, user, password, lun): res = self.connection.SR.introduce(self.session_uuid, lun, name, "iSCSI SR [%s (%s)]" % (host, targetiqn), "lvmoiscsi", "", True, {}) print res pbd = { "uuid" : "", "host" : ref, "SR" : res['Value'], "device_config" : { "port" : port, "target" : host, "SCSIid" : scsiid, "targetIQN" : targetiqn }, "currently_attached" : False, "other_config" : {} } if user: pbd["device_config"]["chapuser"] = user if password: pbd["device_config"]["chappassword"] = password res = self.connection.PBD.create(self.session_uuid, pbd) print res print self.connection.Async.PBD.plug(self.session_uuid, res['Value']) """ sr = { "port" : port, "target" : host, "SCSIid" : scsiid, "targetIQN" : targetiqn } if user: sr["chapuser"] = user if password: sr["chappassword"] = password res = self.connection.Async.SR.create(self.session_uuid, ref, sr, "0", name, "iSCSI SR [%s (%s)]" % (host, targetiqn), "lvmoiscsi", "", True, {}) """ def scan_aoe(self, ref, lista, path): sr = { "device" : path, } value = self.connection.Async.SR.probe(self.session_uuid, ref, sr, "lvm", {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] print task if task['result'].count(""): result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("SRlist") if len(nodes[0].childNodes): for i in range(1,len(nodes[0].childNodes),2): ref = nodes[0].childNodes[i].childNodes[1].childNodes[0].data.strip() print ref print self.search_storage_uuid(ref) if self.search_storage_uuid(ref) == False: lista.append([ref, ref]) if lista.__len__() > 0: return 2 else: return 1 else: if len(task["error_info"]) > 2: self.wine.show_error_dlg(task["error_info"][2]) else: self.wine.show_error_dlg(task["error_info"][1]) self.connection.task.destroy(self.session_uuid, value) return 0 def scan_nfs_vhd(self, ref, list, host, path, options): sr = { "serverpath" : path, "server" : host, "options" : options, } value = self.connection.Async.SR.probe(self.session_uuid, ref, sr, "nfs", {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task['result'].count(""): result = saxutils.unescape(task['result']).replace("","").replace("","").replace(""", '"') dom = xml.dom.minidom.parseString(result) nodes = dom.getElementsByTagName("SRlist") if len(nodes[0].childNodes): for i in range(1,len(nodes[0].childNodes),2): ref = nodes[0].childNodes[i].childNodes[1].childNodes[0].data.strip() if self.search_storage_uuid(ref) == False: list.append([ref, ref]) if list.__len__() > 0: return 2 else: return 1 else: self.wine.show_error_dlg(task["error_info"][2]) self.connection.task.destroy(self.session_uuid, value) return 0 def search_storage_uuid(self, uuid): """ Function to search a storage with specify uuid, returns True if found """ for stg in self.all_storage.keys(): if self.all_storage[stg]["uuid"] == uuid: return True return False def fill_iscsi_target_iqn(self, ref, list, target, port, user=None, password=None): list.clear() sr = { "port" : port, "target": target, } if user: sr["chapuser"] = user if password: sr["chappassword"] = password value = self.connection.Async.SR.create(self.session_uuid, ref, sr, "0", "__gui__", "SHOULD NEVER BE CREATED","lvmoiscsi","user", True, {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["error_info"][3]: dom = xml.dom.minidom.parseString(task["error_info"][3]) nodes = dom.getElementsByTagName("TGT") ix = 1 for i in range(0, len(nodes)): index = nodes[i].childNodes[1].childNodes[0].data.strip() ip = nodes[i].childNodes[3].childNodes[0].data.strip() target = nodes[i].childNodes[5].childNodes[0].data.strip() list.append([target, "%s (%s)" % (target, ip)]) self.connection.task.destroy(self.session_uuid, value) return True else: self.wine.show_error_dlg(task["error_info"][2]) self.connection.task.destroy(self.session_uuid, value) return False def fill_iscsi_target_lun(self, ref, list, target, targetiqn, port, user=None, password=None): list.clear() sr = { "port" : port, "target": target, } # chapuser # chappassword if user: sr["chapuser"] = user if password: sr["chappassword"] = password sr["targetIQN"] = targetiqn value = self.connection.Async.SR.create(self.session_uuid, ref, sr, "0", "__gui__", "SHOULD NEVER BE CREATED","lvmoiscsi","user", True, {})['Value'] task = self.connection.task.get_record(self.session_uuid, value)['Value'] while task["status"] == "pending": task = self.connection.task.get_record(self.session_uuid, value)['Value'] if task["error_info"][3]: dom = xml.dom.minidom.parseString(task["error_info"][3]) nodes = dom.getElementsByTagName("LUN") for i in range(0, len(nodes)): vendor = nodes[i].getElementsByTagName("vendor")[0].childNodes[0].data.strip() #serial = nodes[i].getElementsByTagName("serial")[0].childNodes[0].data.strip() lunid = nodes[i].getElementsByTagName("LUNid")[0].childNodes[0].data.strip() size = nodes[i].getElementsByTagName("size")[0].childNodes[0].data.strip() scsiid = nodes[i].getElementsByTagName("SCSIid")[0].childNodes[0].data.strip() list.append([scsiid, "LUN %s: %s (%s)" % (lunid, self.convert_bytes(size), vendor)]) self.connection.task.destroy(self.session_uuid, value) return True else: self.wine.show_error_dlg(task["error_info"][2]) self.connection.task.destroy(self.session_uuid, value) return False openxenmanager/window_newvm.py0000644000175000017500000006007211636446664015405 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import datetime import gtk from threading import Thread class oxcWindowNewVm: """ Class to manage "new vm" window """ def on_finishwindownewvm_clicked(self, widget, data=None): """ Function called when you press "Finish" on newvm window """ treetemplates = self.builder.get_object("treetemplates") listtemplates = self.modelfiltertpl treenewvmhosts = self.builder.get_object("treenewvmhosts") listnewvmhosts = self.builder.get_object("listnewvmhosts") selection = treetemplates.get_selection() if selection.get_selected()[1] == None: iter = listtemplates.get_iter((0,1)) else: iter = selection.get_selected()[1] ref = listtemplates.get_value(iter, 2) selection = treenewvmhosts.get_selection() if selection.get_selected()[1] == None: iter = listnewvmhosts.get_iter((0,1)) else: iter = selection.get_selected()[1] host = listnewvmhosts.get_value(iter, 3) newvmdata = {} newvmdata['ref'] = ref newvmdata['host'] = host newvmdata['name'] = self.builder.get_object("entrynewvmname").get_text() tb = self.builder.get_object("entrynewvmdescription").get_buffer() newvmdata['description'] = tb.get_text(tb.get_start_iter(), tb.get_end_iter()) newvmdata['startvm'] = self.builder.get_object("checkstartvm").get_active() newvmdata['location_url'] = self.builder.get_object("radiobutton1_data").get_text() newvmdata['location'] = self.newvmdata['location'] data = self.builder.get_object(newvmdata['location'] + "_data") if newvmdata['location'] != "radiobutton1" and data and data.get_active_iter(): newvmdata['vdi'] = data.get_model().get_value(data.get_active_iter(), 1) else: newvmdata['vdi'] = None newvmdata['numberofvcpus'] = self.builder.get_object("numberofvcpus").get_value() newvmdata['memorymb'] = self.builder.get_object("initialmemory").get_value() newvmdata['entrybootparameters'] = self.builder.get_object("entrybootparameters").get_text() Thread(target=self.xc_servers[self.selected_host].create_newvm, args=(newvmdata,)).start() self.builder.get_object("window_newvm").hide() def on_nextwindownewvm_clicked(self, widget, data=None): """ Function called when you press "Next" on newvm window """ tabboxnewvm = self.builder.get_object("tabboxnewvm") if not self.builder.get_object("lblnewvm2").get_property("visible") \ and tabboxnewvm.get_current_page() == 1: step = 2 else: step = 1 tabboxnewvm.set_current_page(tabboxnewvm.get_current_page()+step) exlabel = self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page()-step)).get_label() label = self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page())).get_label() exlabel = exlabel.replace('', '').replace('', '').replace('', '').replace('', '') exlabel = " %-35s" % exlabel.strip() label = ' %-35s' % label[2:] self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page()-step)).set_markup(exlabel) self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page())).set_markup(label) def on_previouswindownewvm_clicked(self, widget, data=None): """ Function called when you press "Previous" on newvm window """ tabboxnewvm = self.builder.get_object("tabboxnewvm") if not self.builder.get_object("lblnewvm2").get_property("visible") \ and tabboxnewvm.get_current_page() == 3: step = 2 else: step = 1 tabboxnewvm.set_current_page(tabboxnewvm.get_current_page()-step) exlabel = self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page()+1)).get_label() label = self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page())).get_label() exlabel = exlabel.replace('', '').replace('', '').replace('', '').replace('', '') exlabel = " %-35s" % exlabel.strip() label = ' %-35s' % label[2:] self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page()+step)).set_markup(exlabel) self.builder.get_object("lblnewvm" + str(tabboxnewvm.get_current_page())).set_markup(label) def on_cancelwindownewvm_clicked(self, widget, data=None): """ Function called when you press "Cancel" on newvm window """ self.builder.get_object("window_newvm").hide() def on_tabboxnewvm_switch_page(self, widget, data=None, data2=None): """ Function called when you press "Switch page" on newvm tabbox """ self.builder.get_object("previouswindownewvm").set_sensitive(widget.get_current_page() > 0) self.builder.get_object("nextwindownewvm").set_sensitive(widget.get_current_page() != 7) self.builder.get_object("finishwindownewvm").set_sensitive(widget.get_current_page() == 7) if widget.get_current_page() == 1: listnewvmstorage = self.builder.get_object("listnewvmstorage") treetemplates = self.builder.get_object("treetemplates") listtemplates = self.modelfiltertpl treenewvmhosts = self.builder.get_object("treenewvmhosts") listnewvmhosts = self.builder.get_object("listnewvmhosts") selection = treetemplates.get_selection() if selection.get_selected()[1] == None: iter = listtemplates.get_iter((0,1)) else: iter = selection.get_selected()[1] ref = listtemplates.get_value(iter, 2) selection = treenewvmhosts.get_selection() if selection.get_selected()[1] == None: iter = listnewvmhosts.get_iter((0,1)) else: iter = selection.get_selected()[1] host = listnewvmhosts.get_value(iter, 3) self.xc_servers[self.selected_host].fill_listnewvmstorage(listnewvmstorage, ref, host, self.xc_servers[self.selected_host].default_sr) # Fill list new disk listnewvmstorage = self.builder.get_object("listnewvmstorage") treenewvmstorage = self.builder.get_object("treenewvmstorage") listnewvmdisk = self.builder.get_object("listnewvmdisk") treenewvmdisk = self.builder.get_object("treenewvmdisk") # And fill disks from template information defsr = self.xc_servers[self.selected_host].fill_listnewvmdisk(listnewvmdisk, host) # And set the selection in the first disk treenewvmstorage.set_cursor((defsr,0), treenewvmstorage.get_column(0)) treenewvmstorage.get_selection().select_path((defsr,0)) treenewvmdisk.set_cursor((defsr,0), treenewvmdisk.get_column(0)) treenewvmdisk.get_selection().select_path((defsr,0)) # Installation method self.builder.get_object("entrybootparameters").set_text( self.xc_servers[self.selected_host].all_vms[ref]['PV_args'] ) # Get "other_config" from selected template, other_config contains information about install other_config = self.xc_servers[self.selected_host].all_vms[ref]['other_config'] # If other_config contains "install-methods" (ftp, nfs, http) or via cdrom if "install-methods" in other_config: methods = other_config["install-methods"] # If contains nfs/http or ftp, show fields if methods.count("nfs") == 0 and methods.count("http") == 0 and methods.count("ftp") == 0: self.builder.get_object("radiobutton1_data").hide() self.builder.get_object("radiobutton1").hide() self.builder.get_object("fixed22").hide() self.builder.get_object("radiobutton1").set_active(False) self.builder.get_object("radiobutton2").set_active(True) self.builder.get_object("radiobutton2_data").set_sensitive(True) self.builder.get_object("radiobutton2_data").set_active(0) else: # Else disable installation via network self.builder.get_object("radiobutton1_data").show() self.builder.get_object("radiobutton1").show() self.builder.get_object("fixed22").show() self.builder.get_object("radiobutton1").set_active(True) self.builder.get_object("radiobutton2").set_active(False) self.builder.get_object("radiobutton2_data").set_sensitive(False) def on_cancelnewvmdisk_clicked(self, widget, data=None): """ Function called when you cancel the "new disk" window on "new vm" assistant """ newvmdisk = self.builder.get_object("newvmdisk") newvmdisk.hide() def on_addnewvmstorage_clicked(self, widget, data=None): """ Function called when you press "new disk" window on "new vm" assistant """ newvmdisk = self.builder.get_object("newvmdisk") self.newvmdata['storage_editing'] = False newvmdisk.show() def on_deletenewvmnetwork_clicked(self, widget, data=None): """ Function called when you remove a network interface from new vm networks list """ treenewvmnetwork = self.builder.get_object("treenewvmnetwork") listnewvmnetwork = self.builder.get_object("listnewvmnetworks") selection = treenewvmnetwork.get_selection() if selection.get_selected()[1] == None: iter = listnewvmnetwork.get_iter((0,1)) else: iter = selection.get_selected()[1] self.listnetworks.remove(iter) def on_addnewvmnetwork_clicked(self, widget, data=None): """ Function called when add a new network """ # Increase n_networks value n_networks = self.listnetworks.__len__()+1 # Get the first network by default network = self.xc_servers[self.selected_host].first_network() # Get the ref of first network network_ref = self.xc_servers[self.selected_host].first_network_ref() # Add to list self.listnetworks.append(["interface " + str(n_networks), "auto-generated", network, network_ref ]) def on_editnewvmstorage_clicked(self, widget, data=None): """ Function called when you press edit storage on "new vm" process """ newvmdisk = self.builder.get_object("newvmdisk") self.newvmdata['storage_editing'] = True # Set the current values to differents elements listnewvmstorage = self.builder.get_object("listnewvmstorage") self.builder.get_object("disksize").set_value( float(listnewvmstorage.get_value(self.newvmdata['last_diskiter_selected'],0))) treenewvmdisk = self.builder.get_object("treenewvmdisk") listnewvmdisk = self.builder.get_object("listnewvmdisk") selection = treenewvmdisk.get_selection() ref = listnewvmstorage.get_value(self.newvmdata['last_diskiter_selected'],3) for i in range(0, listnewvmdisk.__len__()): if ref == listnewvmdisk.get_value(listnewvmdisk.get_iter((i,)), 4): print ref treenewvmdisk.set_cursor((i,), treenewvmdisk.get_column(1)) treenewvmdisk.get_selection().select_path((i,)) # Show a "new vm disk" window newvmdisk.show() def on_acceptnewvmdisk_clicked(self, widget, data=None): """ Function called when you press add a new storage on "new vm" process or accept "edit storage" """ listnewvmstorage = self.builder.get_object("listnewvmstorage") size = self.builder.get_object("disksize").get_value() disk = self.newvmdata['last_disk_selected'] disk_name = self.xc_servers[self.selected_host].all_storage[disk]['name_label'] + " on " \ + self.xc_servers[self.selected_host].all_hosts[self.newvmdata['host']]['name_label'] shared = str(self.xc_servers[self.selected_host].all_storage[disk]['shared']) if self.newvmdata['storage_editing'] == False: # If we are not editing, then add to list listnewvmstorage.append([size, disk_name, shared, disk]) else: # Else update values from list iter = self.newvmdata['last_diskiter_selected'] listnewvmstorage.set_value(iter, 0, size) listnewvmstorage.set_value(iter, 1, disk_name) listnewvmstorage.set_value(iter, 2, shared) listnewvmstorage.set_value(iter, 3, disk) # Hide the window self.builder.get_object("newvmdisk").hide() def on_deletenewvmstorage_clicked(self, widget, data=None): """ Function called when you delete a storage on "new vm" process """ listnewvmstorage = self.builder.get_object("listnewvmstorage") # Remove from list listnewvmstorage.remove(self.newvmdata['last_diskiter_selected']) def on_newvmtreeviewhosts_row_activated(self, widget, data=None): """ Function called when you select a host on "new vm" assistant """ listnewvmhosts = self.builder.get_object("listnewvmhosts") treenewvmhosts = self.builder.get_object("treenewvmhosts") selection = treenewvmhosts.get_selection() # Get selected host if selection.get_selected()[1] == None: # If none selected, get the first available host iter = listnewvmhosts.get_iter((self.xc_servers[self.selected_host].get_path_available_host(),1)) else: iter = selection.get_selected()[1] # Set reference host to "newvmdata" struct self.newvmdata['host'] = listnewvmhosts.get_value(iter, 3) host= self.xc_servers[self.selected_host].all_hosts[self.newvmdata['host']] host_metrics = self.xc_servers[self.selected_host].all_host_metrics[host['metrics']] # Fill host info on "set memory/vcpu" window (only for information) self.builder.get_object("lblnewvmhost").set_label(host['name_label']) self.builder.get_object("lblnewvmcpus").set_label(str(len(host['host_CPUs']))) self.builder.get_object("lblnewvmtotalmemory").set_label(self.convert_bytes(host_metrics['memory_total'])) self.builder.get_object("lblnewvmfreememory").set_label(self.convert_bytes(host_metrics['memory_free'])) def on_treetemplates_row_activated(self, widget, data=None, data2=None): """ Function called when you select a template to create a new vm """ treetemplates = self.builder.get_object("treetemplates") listtemplates = self.modelfiltertpl selection = treetemplates.get_selection() # Get the selected template if selection.get_selected()[1] == None: # Get the first template if selection is empty iter = listtemplates.get_iter((0,)) else: iter = selection.get_selected()[1] # Fill template info self.builder.get_object("lblnewvmname").set_label(listtemplates.get_value(iter, 1)) self.builder.get_object("lblnewvminfo").set_label( self.xc_servers[self.selected_host].all_vms[listtemplates.get_value(iter, 2)]['name_description']) self.builder.get_object("entrynewvmname").set_text(listtemplates.get_value(iter, 1) + " (" + str(datetime.date.today()) + ")") ref = listtemplates.get_value(iter, 2) vm = self.xc_servers[self.selected_host].all_vms[ref] self.builder.get_object("numberofvcpus").set_value(float(int(vm['VCPUs_max']))) self.builder.get_object("initialmemory").set_value(float(int(vm['memory_static_max'])/1024/1024)) self.builder.get_object("disksize").set_value(float(5)) # Check if selected template should be installed with cd or not # If postinstall is true, then will be cloned, else you will be specifiy a method of installation if "postinstall" in self.xc_servers[self.selected_host].all_vms[listtemplates.get_value(iter, 2)]['other_config'] or \ self.xc_servers[self.selected_host].all_vms[listtemplates.get_value(iter, 2)]['last_booted_record'] != "": self.builder.get_object("lblnewvm2").hide() else: self.builder.get_object("lblnewvm2").show() def on_treenewvmdisk_row_activated(self, widget, data=None, data2=None): """ Function called when you select a disk on "new disk" in "new vm" assistant only for set "last_disk_selected" variable """ treenewvmdisk = self.builder.get_object("treenewvmdisk") listnewvmdisk = self.builder.get_object("listnewvmdisk") selection = treenewvmdisk.get_selection() # Get the selected disk if selection.get_selected()[1] == None: # Or get the first iter = listnewvmdisk.get_iter((0,1)) else: iter = selection.get_selected()[1] # Set inside newvmdata struct self.newvmdata['last_disk_selected'] = listnewvmdisk.get_value(iter, 4) def on_treenewvmstorage_row_activated(self, widget, data=None, data2=None): """ Function called when you select a disk, is used to set a variable called "last_disk_selected" """ treenewvmstorage = self.builder.get_object("treenewvmstorage") listnewvmstorage = self.builder.get_object("listnewvmstorage") selection = treenewvmstorage.get_selection() # Get the selected disk selection.set_mode(gtk.SELECTION_SINGLE) if selection.get_selected()[1] == None: if listnewvmstorage.__len__() > 0: # Or get the first iter = listnewvmstorage.get_iter((0,)) else: iter = None else: iter = selection.get_selected()[1] # Set inside newvmdata struct self.newvmdata['last_diskiter_selected'] = iter def on_treenewvmnetwork_cursor_changed(self, widget, data=None, data2=None): """ Function called when you select a network, is used to set a variable called "last_networkiter_selected" """ treenewvmnetwork = self.builder.get_object("treenewvmnetwork") listnewvmnetwork = self.builder.get_object("listnewvmnetworks") selection = treenewvmnetwork.get_selection() selection.set_mode(gtk.SELECTION_SINGLE) if selection.get_selected()[1] == None: iter = listnewvmnetwork.get_iter((0,1)) else: iter = selection.get_selected()[1] self.newvmdata['last_networkiter_selected'] = iter def on_newvm_prepare(self, widget, data=None): """ Function called when you select a network, is used to set a variable called "last_networkiter_selected" """ if widget.get_current_page() == 2: # If page is "installation method" then enable/disable installation options # Set the boot parameters in specify label from selected template pass if widget.get_current_page() == 3: # After specify name and description.. set to newvmdata struct tb = self.builder.get_object("entrynewvmdescription").get_buffer() self.newvmdata['name'] = self.builder.get_object("entrynewvmname").get_text() self.newvmdata['description'] = tb.get_text(tb.get_start_iter(), tb.get_end_iter()) if widget.get_current_page() == 3: # After the choose installation method.. set the location on newvmdata struct self.newvmdata['location_url'] = self.builder.get_object("radiobutton1_data").get_text() if self.newvmdata['location'] != "radiobutton1": data = self.builder.get_object(self.newvmdata['location'] + "_data") self.newvmdata['vdi'] = data.get_model().get_value(data.get_active_iter(), 1) else: self.newvmdata['vdi'] = None #print self.newvmdata['vdi'] #print "Elegido location" if widget.get_current_page() == 4: # After choose installation method, set vcpus and memory from template information if "ref" in self.newvmdata: vm = self.xc_servers[self.selected_host].all_vms[self.newvmdata['ref']] self.builder.get_object("numberofvcpus").set_value(float(int(vm['VCPUs_max']))) self.builder.get_object("initialmemory").set_value(float(int(vm['memory_static_max'])/1024/1024)) self.builder.get_object("disksize").set_value(float(5)) if widget.get_current_page() == 5: # After specify number of VCPUs, memory and boot parameters, set into newvmdata struct self.newvmdata['numberofvcpus'] = self.builder.get_object("numberofvcpus").get_value() self.newvmdata['memorymb'] = self.builder.get_object("initialmemory").get_value() self.newvmdata['entrybootparameters'] = self.builder.get_object("entrybootparameters").get_text() #print "Elegido number of vcpus and memory" self.newvm.set_page_complete(widget.get_nth_page(widget.get_current_page()), True) def forward_page(self, current_page, user_data): """ Function called to know what is the next page """ if current_page == 1: # If has not installation method (template clone), then step to page 3 if "ref" in self.newvmdata: if "postinstall" in self.xc_servers[self.selected_host].all_vms[self.newvmdata['ref']]['other_config']: return current_page+2 # Else only increment one to actual page return current_page+1 def on_radiobutton1_activate(self, widget, data=None): """ FIXME: enable or disable elements depends the selection """ if widget.state == 2: for data in ['radiobutton1', 'radiobutton2', 'radiobutton3']: if data == gtk.Buildable.get_name(widget): self.builder.get_object(data + "_data").set_sensitive(True) if data != "radiobutton1": if self.builder.get_object(data + "_data").get_active() == -1: self.builder.get_object(data + "_data").set_active(0) else: self.builder.get_object(data + "_data").set_sensitive(False) self.newvmdata['location'] = gtk.Buildable.get_name(widget) def on_networkcolumn_changed(self, widget, data=None, data2=None): """ Function called when you change the "network" listbox on list of networks """ if "last_networkiter_selected" in self.newvmdata: self.listnetworks.set_value(self.newvmdata['last_networkiter_selected'], 2, self.listnetworkcolumn.get_value(data2, 0)) self.listnetworks.set_value(self.newvmdata['last_networkiter_selected'], 3, self.listnetworkcolumn.get_value(data2, 1)) openxenmanager/rrd.py0000644000175000017500000002015111636446664013443 0ustar rrsrrs#!/usr/bin/env python # ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xml.dom.minidom import pygtk_chart class XPORT: def __init__(self, filename): """ Read file and parse head/ds """ f = open(filename, "r") self.data= f.read() f.close() self.parse_head() self.parse_ds() def parse_head(self): """ Get "rows", "step" and "columns" from rrd """ self.dom = xml.dom.minidom.parseString(self.data) del self.data self.rrdinfo = {} for field in ["rows", "step", "columns"]: if self.dom.getElementsByTagName(field)[0].childNodes: self.rrdinfo[field] = self.dom.getElementsByTagName(field)[0].childNodes[0].data else: self.rrdinfo[field] = 0 def parse_ds(self): """ Get "ds" (memory, cpu0..) from rrd """ ds_nodes = self.dom.getElementsByTagName("entry") self.rrdinfo["ds"] = {} self.keys = [] for ds_node in ds_nodes: name = ds_node.childNodes[0].data.split(":")[3] if name in self.keys: name = "1" + name self.keys.append(name) self.rrdinfo["ds"][name] = {} self.rrdinfo["ds"][name]['values'] = [] self.rrdinfo["ds"][name]['max_value'] = 0 def get_data(self): """ Function to get data array (timestamp, value) for all DS """ for row in self.dom.getElementsByTagName("row"): i = 0 lastupdate = row.childNodes[0].childNodes[0].data for values in row.childNodes[1:]: value = float(values.childNodes[0].data) if value == value and value != float('inf'): self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), value]) elif self.keys[i] == "memory_internal_free": self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), 0]) elif self.keys[i] == "memory": self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), 0]) if value != float('inf'): if self.rrdinfo["ds"][self.keys[i]]['max_value'] < value: self.rrdinfo["ds"][self.keys[i]]['max_value'] = value i = i + 1 return self.rrdinfo["ds"] class RRD: def __init__(self, filename): """ Read file and parse head/ds """ f = open(filename, "r") self.data= f.read() f.close() self.parse_head() self.parse_ds() def parse_head(self): """ Get "version", "step" and "lastupdate" from rrd """ self.dom = xml.dom.minidom.parseString(self.data) del self.data self.rrdinfo = {} for field in ["version", "step", "lastupdate"]: self.rrdinfo[field] = self.dom.getElementsByTagName(field)[0].childNodes[0].data def parse_ds(self): """ Get "ds" (memory, cpu0..) from rrd """ ds_nodes = self.dom.getElementsByTagName("ds") self.rrdinfo["ds"] = {} self.keys = [] for ds_node in ds_nodes: if ds_node.getElementsByTagName("name"): name = ds_node.getElementsByTagName("name")[0].childNodes[0].data if name in self.keys: name = "1" + name self.keys.append(name) self.rrdinfo["ds"][name] = {} for field in ["type", "minimal_heartbeat", "min", "max", "last_ds", "value", "unknown_sec"]: self.rrdinfo["ds"][name][field] = ds_node.getElementsByTagName(field)[0].childNodes[0].data self.rrdinfo["ds"][name]['values'] = [] self.rrdinfo["ds"][name]['max_value'] = 0 def get_data(self, pdp=5): """ Function to get data array (timestamp, value) for all DS, filter by pdp (seconds) """ lastupdate = int(self.rrdinfo["lastupdate"]) for rra in self.dom.getElementsByTagName("rra"): step = int(rra.getElementsByTagName("pdp_per_row")[0].childNodes[0].data)*int(self.rrdinfo["step"]) if step == pdp: database = rra.getElementsByTagName("database")[0] lastupdate = int(self.rrdinfo["lastupdate"]) - (int(self.rrdinfo["lastupdate"]) % step) lastupdate = lastupdate - (len(database.getElementsByTagName("row")) * step) for row in database.getElementsByTagName("row"): i = 0 lastupdate = lastupdate + step for value in row.childNodes: value = float(value.childNodes[0].data) if value == value and value != float('inf'): self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), value]) elif self.keys[i] == "memory_internal_free": self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), 0]) elif self.keys[i] == "memory": self.rrdinfo["ds"][self.keys[i]]['values'].append([int(lastupdate), 0]) if value != float('inf'): if self.rrdinfo["ds"][self.keys[i]]['max_value'] < value: self.rrdinfo["ds"][self.keys[i]]['max_value'] = value i = i + 1 return self.rrdinfo["ds"] """ window = gtk.Window() window.connect("destroy", gtk.main_quit) window.resize(500, 300) window.set_size_request(500, 200) chart = line_chart.LineChart() from time import strftime, localtime count = 0 def prueba(value): if strftime("%S", localtime(value)) == "00": return strftime("%H:%M", localtime(value)) else: return "" def hovered(chart, graph, (x, y)): print strftime("%H:%M:%S", localtime(x)), y chart.xaxis.set_show_tics(True) chart.xaxis.set_show_label(True) chart.xaxis.set_tic_format_function(prueba) chart.yaxis.set_position(7) chart.connect("datapoint-hovered", hovered) chart.legend.set_visible(False) chart.legend.set_position(line_chart.POSITION_BOTTOM_RIGHT) rrd = RRD("prueba2.rrd") rrdinfo = rrd.get_data(5) def dump(obj): Internal use only for attr in dir(obj): print "obj.%s = %s" % (attr, getattr(obj, attr)) #Memory chart.set_yrange((0, 256)) data = rrdinfo["memory"]["values"] data2 = rrdinfo["memory_internal_free"]["values"] for i in range(len(data)): data[i][1] = (data[i][1] - data2[i][1]*1024)/1024/1024 #cpu0 chart.set_yrange((0, 1.0)) #vif_0_tx max_value = 0 for key in rrdinfo.keys(): if key[:3] == "vif": data = rrdinfo[key]["values"] for i in range(len(data)): data[i][1] = data[i][1]/1024 if rrdinfo[key]['max_value']/1024 > max_value: max_value = rrdinfo[key]['max_value']/1024 graph_a = line_chart.Graph(key[4:], key[4:], data) dump(graph_a) chart.add_graph(graph_a) chart.set_yrange((0, max_value)) scrolled = gtk.ScrolledWindow() viewport = gtk.Viewport() chart.set_size_request(len(data)*10, 200) scrolled.add(viewport) chart2 = chart scrolled.add_with_viewport(chart) window.add(scrolled) window.show_all() gtk.main() """ openxenmanager/LICENSE0000644000175000017500000003543311636446664013320 0ustar rrsrrs GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS openxenmanager/openxenmanager0000755000175000017500000000003511636446664015236 0ustar rrsrrs#!/bin/bash python window.py openxenmanager/pygtk_chart/0000755000175000017500000000000011636446664014622 5ustar rrsrrsopenxenmanager/pygtk_chart/data/0000755000175000017500000000000011636446664015533 5ustar rrsrrsopenxenmanager/pygtk_chart/data/tango.color0000644000175000017500000000007011636446664017700 0ustar rrsrrs#cc0000 #3465a4 #73d216 #f57900 #75507b #c17d11 #edd400 openxenmanager/pygtk_chart/line_chart.py0000755000175000017500000023127211636446664017316 0ustar rrsrrs#!/usr/bin/env python # # lineplot.py # # Copyright 2008 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ Contains the LineChart widget. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import gobject import cairo import gtk import math import os import pygtk_chart from pygtk_chart.basics import * from pygtk_chart.chart_object import ChartObject from pygtk_chart import chart from pygtk_chart import label from pygtk_chart import COLORS, COLOR_AUTO RANGE_AUTO = 0 GRAPH_PADDING = 1 / 15.0 #a relative padding GRAPH_POINTS = 1 GRAPH_LINES = 2 GRAPH_BOTH = 3 COLOR_AUTO = 4 POSITION_AUTO = 5 POSITION_LEFT = 6 POSITION_RIGHT = 7 POSITION_BOTTOM = 6 POSITION_TOP = 7 POSITION_TOP_RIGHT = 8 POSITION_BOTTOM_RIGHT = 9 POSITION_BOTTOM_LEFT = 10 POSITION_TOP_LEFT = 11 def draw_point(context, x, y, radius, style): a = radius / 1.414 #1.414=sqrt(2) if style == pygtk_chart.POINT_STYLE_CIRCLE: context.arc(x, y, radius, 0, 2 * math.pi) context.fill() elif style == pygtk_chart.POINT_STYLE_SQUARE: context.rectangle(x - a, y- a, 2 * a, 2 * a) context.fill() elif style == pygtk_chart.POINT_STYLE_CROSS: context.move_to(x, y - a) context.rel_line_to(0, 2 * a) context.stroke() context.move_to(x - a, y) context.rel_line_to(2 * a, 0) context.stroke() elif style == pygtk_chart.POINT_STYLE_TRIANGLE_UP: a = 1.732 * radius #1.732=sqrt(3) b = a / (2 * 1.732) context.move_to(x - a / 2, y + b) context.rel_line_to(a, 0) context.rel_line_to(-a / 2, -(radius + b)) context.rel_line_to(-a / 2, radius + b) context.close_path() context.fill() elif style == pygtk_chart.POINT_STYLE_TRIANGLE_DOWN: a = 1.732 * radius #1.732=sqrt(3) b = a / (2 * 1.732) context.move_to(x - a / 2, y - b) context.rel_line_to(a, 0) context.rel_line_to(-a / 2, radius + b) context.rel_line_to(-a / 2, -(radius + b)) context.close_path() context.fill() elif style == pygtk_chart.POINT_STYLE_DIAMOND: context.move_to(x, y - a) context.rel_line_to(a, a) context.rel_line_to(-a, a) context.rel_line_to(-a, -a) context.rel_line_to(a, -a) context.fill() def draw_point_pixbuf(context, x, y, pixbuf): w = pixbuf.get_width() h = pixbuf.get_height() ax = x - w / 2 ay = y - h / 2 context.set_source_pixbuf(pixbuf, ax, ay) context.rectangle(ax, ay, w, h) context.fill() def draw_errors(context, rect, range_calc, x, y, errors, draw_x, draw_y, xaxis, yaxis, size): if (x, y) in errors: xerror, yerror = errors[(x, y)] if draw_x and xerror > 0: #rect, x, y, xaxis, yaxis left = range_calc.get_absolute_point(rect, x - xerror, y, xaxis, yaxis) right = range_calc.get_absolute_point(rect, x + xerror, y, xaxis, yaxis) context.move_to(left[0], left[1]) context.line_to(right[0], right[1]) context.stroke() context.move_to(left[0], left[1] - size) context.rel_line_to(0, 2 * size) context.stroke() context.move_to(right[0], right[1] - size) context.rel_line_to(0, 2 * size) context.stroke() if draw_y and yerror > 0: top = range_calc.get_absolute_point(rect, x, y - yerror, xaxis, yaxis) bottom = range_calc.get_absolute_point(rect, x, y + yerror, xaxis, yaxis) context.move_to(top[0], top[1]) context.line_to(bottom[0], bottom[1]) context.stroke() context.move_to(top[0] - size, top[1]) context.rel_line_to(2 * size, 0) context.stroke() context.move_to(bottom[0] - size, bottom[1]) context.rel_line_to(2 * size, 0) context.stroke() def separate_data_and_errors(old_data): data = [] errors = {} for d in old_data: if len(d) == 2: data.append(d) elif len(d) == 4: data.append((d[0], d[1])) errors[(d[0], d[1])] = (d[2], d[3]) return data, errors class RangeCalculator: """ This helper class calculates ranges. It is used by the LineChart widget internally, there is no need to create an instance yourself. """ def __init__(self): self._data_xrange = None self._data_yrange = None self._xrange = RANGE_AUTO self._yrange = RANGE_AUTO self._cached_xtics = [] self._cached_ytics = [] def add_graph(self, graph): if self._data_xrange == None: self._data_yrange = graph.get_y_range() self._data_xrange = graph.get_x_range() else: yrange = graph.get_y_range() xrange = graph.get_x_range() if xrange and yrange: xmin = min(xrange[0], self._data_xrange[0]) xmax = max(xrange[1], self._data_xrange[1]) ymin = min(yrange[0], self._data_yrange[0]) ymax = max(yrange[1], self._data_yrange[1]) self._data_xrange = (xmin, xmax) self._data_yrange = (ymin, ymax) def get_ranges(self, xaxis, yaxis): xrange = self._xrange if xrange == RANGE_AUTO: xrange = self._data_xrange if xrange[0] == xrange[1]: xrange = (xrange[0], xrange[0] + 0.1) yrange = self._yrange if yrange == RANGE_AUTO: yrange = self._data_yrange if yrange[0] == yrange[1]: yrange = (yrange[0], yrange[0] + 0.1) if xaxis.get_logarithmic(): xrange = math.log10(xrange[0]), math.log10(xrange[1]) if yaxis.get_logarithmic(): yrange = math.log10(yrange[0]), math.log10(yrange[1]) return (xrange, yrange) def set_xrange(self, xrange): self._xrange = xrange def set_yrange(self, yrange): self._yrange = yrange def get_absolute_zero(self, rect, xaxis, yaxis): xrange, yrange = self.get_ranges(xaxis, yaxis) xfactor = float(rect.width * (1 - 2 * GRAPH_PADDING)) / (xrange[1] - xrange[0]) yfactor = float(rect.height * (1 - 2 * GRAPH_PADDING)) / (yrange[1] - yrange[0]) zx = (rect.width * GRAPH_PADDING) - xrange[0] * xfactor zy = rect.height - ((rect.height * GRAPH_PADDING) - yrange[0] * yfactor) return (zx,zy) def get_absolute_point(self, rect, x, y, xaxis, yaxis): (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) xrange, yrange = self.get_ranges(xaxis, yaxis) xfactor = float(rect.width * (1 - 2 * GRAPH_PADDING)) / (xrange[1] - xrange[0]) yfactor = float(rect.height * (1 - 2 * GRAPH_PADDING)) / (yrange[1] - yrange[0]) ax = zx + x * xfactor ay = zy - y * yfactor return (ax, ay) def prepare_tics(self, rect, xaxis, yaxis): self._cached_xtics = self._get_xtics(rect, xaxis, yaxis) self._cached_ytics = self._get_ytics(rect, xaxis, yaxis) def get_xtics(self, rect): return self._cached_xtics def get_ytics(self, rect): return self._cached_ytics def _get_xtics(self, rect, xaxis, yaxis): tics = [] (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) (xrange, yrange) = self.get_ranges(xaxis, yaxis) delta = xrange[1] - xrange[0] exp = int(math.log10(delta)) - 1 first_n = int(xrange[0] / (10 ** exp)) last_n = int(xrange[1] / (10 ** exp)) n = last_n - first_n N = rect.width / 50.0 divide_by = int(n / N) if divide_by == 0: divide_by = 1 left = rect.width * GRAPH_PADDING right = rect.width * (1 - GRAPH_PADDING) for i in range(first_n, last_n + 1): num = i * 10 ** exp (x, y) = self.get_absolute_point(rect, num, 0, xaxis, yaxis) if i % divide_by == 0 and is_in_range(x, (left, right)): tics.append(((x, y), num)) return tics def _get_ytics(self, rect, xaxis, yaxis): tics = [] (zx, zy) = self.get_absolute_zero(rect, xaxis, yaxis) (xrange, yrange) = self.get_ranges(xaxis, yaxis) delta = yrange[1] - yrange[0] exp = int(math.log10(delta)) - 1 first_n = int(yrange[0] / (10 ** exp)) last_n = int(yrange[1] / (10 ** exp)) n = last_n - first_n N = rect.height / 50.0 divide_by = int(n / N) if divide_by == 0: divide_by = 1 top = rect.height * GRAPH_PADDING bottom = rect.height * (1 - GRAPH_PADDING) for i in range(first_n, last_n + 1): num = i * 10 ** exp (x, y) = self.get_absolute_point(rect, 0, num, xaxis, yaxis) if i % divide_by == 0 and is_in_range(y, (top, bottom)): tics.append(((x, y), num)) return tics class LineChart(chart.Chart): """ A widget that shows a line chart. The following attributes can be accessed: - LineChart.background (inherited from chart.Chart) - LineChart.title (inherited from chart.Chart) - LineChart.graphs (a dict that holds the graphs identified by their name) - LineChart.grid - LineChart.xaxis - LineChart.yaxis Properties ========== LineChart inherits properties from chart.Chart. Signals ======= The LineChart class inherits signals from chart.Chart. Additional chart: - datapoint-clicked (emitted if a datapoint is clicked) - datapoint-hovered (emitted if a datapoint is hovered with the mouse pointer) Callback signature for both signals: def callback(linechart, graph, (x, y)) """ __gsignals__ = {"datapoint-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), "datapoint-hovered": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))} def __init__(self): chart.Chart.__init__(self) self.graphs = {} self._range_calc = RangeCalculator() self.xaxis = XAxis(self._range_calc) self.yaxis = YAxis(self._range_calc) self.grid = Grid(self._range_calc) self.legend = Legend() self._highlighted_points = [] self.xaxis.connect("appearance_changed", self._cb_appearance_changed) self.yaxis.connect("appearance_changed", self._cb_appearance_changed) self.grid.connect("appearance_changed", self._cb_appearance_changed) self.legend.connect("appearance_changed", self._cb_appearance_changed) def __iter__(self): for name, graph in self.graphs.iteritems(): yield graph def _cb_button_pressed(self, widget, event): points = chart.get_sensitive_areas(event.x, event.y) for x, y, graph in points: self.emit("datapoint-clicked", graph, (x, y)) def _cb_motion_notify(self, widget, event): self._highlighted_points = chart.get_sensitive_areas(event.x, event.y) for x, y, graph in self._highlighted_points: self.emit("datapoint-hovered", graph, (x, y)) self.queue_draw() def _do_draw_graphs(self, context, rect): """ Draw all the graphs. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ for (name, graph) in self.graphs.iteritems(): graph.draw(context, rect, self.xaxis, self.yaxis, self._highlighted_points) self._highlighted_points = [] def _do_draw_axes(self, context, rect): """ Draw x and y axis. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ self.xaxis.draw(context, rect, self.yaxis) self.yaxis.draw(context, rect, self.xaxis) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ label.begin_drawing() chart.init_sensitive_areas() rect = self.get_allocation() self._range_calc.prepare_tics(rect, self.xaxis, self.yaxis) #initial context settings: line width & font context.set_line_width(1) font = gtk.Label().style.font_desc.get_family() context.select_font_face(font,cairo.FONT_SLANT_NORMAL, \ cairo.FONT_WEIGHT_NORMAL) self.draw_basics(context, rect) data_available = False for (name, graph) in self.graphs.iteritems(): if graph.has_something_to_draw(): data_available = True break if self.graphs and data_available: self.grid.draw(context, rect, self.xaxis, self.yaxis) self._do_draw_axes(context, rect) self._do_draw_graphs(context, rect) label.finish_drawing() self.legend.draw(context, rect, self.graphs) def add_graph(self, graph): """ Add a graph object to the plot. @type graph: line_chart.Graph @param graph: The graph to add. """ if graph.get_color() == COLOR_AUTO: graph.set_color(COLORS[len(self.graphs) % len(COLORS)]) graph.set_range_calc(self._range_calc) self.graphs[graph.get_name()] = graph self._range_calc.add_graph(graph) graph.connect("appearance-changed", self._cb_appearance_changed) def remove_graph(self, name): """ Remove a graph from the plot. @type name: string @param name: The name of the graph to remove. """ del self.graphs[name] self.queue_draw() def set_xrange(self, xrange): """ Set the visible xrange. xrange has to be a pair: (xmin, xmax) or RANGE_AUTO. If you set it to RANGE_AUTO, the visible range will be calculated. @type xrange: pair of numbers @param xrange: The new xrange. """ self._range_calc.set_xrange(xrange) self.queue_draw() def get_xrange(self): return self._range_calc.get_ranges(self.xaxis, self.yaxis)[0] def set_yrange(self, yrange): """ Set the visible yrange. yrange has to be a pair: (ymin, ymax) or RANGE_AUTO. If you set it to RANGE_AUTO, the visible range will be calculated. @type yrange: pair of numbers @param yrange: The new yrange. """ self._range_calc.set_yrange(yrange) self.queue_draw() def get_yrange(self): return self._range_calc.get_ranges(self.xaxis, self.yaxis)[1] class Axis(ChartObject): """ This class represents an axis on the line chart. Properties ========== The Axis class inherits properties from chart_object.ChartObject. Additional properties: - label (a label for the axis, type: string) - show-label (sets whether the axis' label should be shown, type: boolean) - position (position of the axis, type: an axis position constant) - show-tics (sets whether tics should be shown at the axis, type: boolean) - show-tic-lables (sets whether labels should be shown at the tics, type: boolean) - tic-format-function (a function that is used to format the tic labels, default: str) - logarithmic (sets whether the axis should use a logarithmic scale, type: boolean). Signals ======= The Axis class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"label": (gobject.TYPE_STRING, "axis label", "The label of the axis.", "", gobject.PARAM_READWRITE), "show-label": (gobject.TYPE_BOOLEAN, "show label", "Set whether to show the axis label.", True, gobject.PARAM_READWRITE), "position": (gobject.TYPE_INT, "axis position", "Position of the axis.", 5, 7, 5, gobject.PARAM_READWRITE), "show-tics": (gobject.TYPE_BOOLEAN, "show tics", "Set whether to draw tics.", True, gobject.PARAM_READWRITE), "show-tic-labels": (gobject.TYPE_BOOLEAN, "show tic labels", "Set whether to draw tic labels", True, gobject.PARAM_READWRITE), "tic-format-function": (gobject.TYPE_PYOBJECT, "tic format function", "This function is used to label the tics.", gobject.PARAM_READWRITE), "logarithmic": (gobject.TYPE_BOOLEAN, "logarithmic scale", "Set whether to use logarithmic scale.", False, gobject.PARAM_READWRITE)} def __init__(self, range_calc, label): ChartObject.__init__(self) self.set_property("antialias", False) self._label = label self._show_label = True self._position = POSITION_AUTO self._show_tics = True self._show_tic_labels = True self._tic_format_function = str self._logarithmic = False self._range_calc = range_calc def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "label": return self._label elif property.name == "show-label": return self._show_label elif property.name == "position": return self._position elif property.name == "show-tics": return self._show_tics elif property.name == "show-tic-labels": return self._show_tic_labels elif property.name == "tic-format-function": return self._tic_format_function elif property.name == "logarithmic": return self._logarithmic else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "label": self._label = value elif property.name == "show-label": self._show_label = value elif property.name == "position": self._position = value elif property.name == "show-tics": self._show_tics = value elif property.name == "show-tic-labels": self._show_tic_labels = value elif property.name == "tic-format-function": self._tic_format_function = value elif property.name == "logarithmic": self._logarithmic = value else: raise AttributeError, "Property %s does not exist." % property.name def set_label(self, label): """ Set the label of the axis. @param label: new label @type label: string. """ self.set_property("label", label) self.emit("appearance_changed") def get_label(self): """ Returns the current label of the axis. @return: string. """ return self.get_property("label") def set_show_label(self, show): """ Set whether to show the axis' label. @type show: boolean. """ self.set_property("show-label", show) self.emit("appearance_changed") def get_show_label(self): """ Returns True if the axis' label is shown. @return: boolean. """ return self.get_property("show-label") def set_position(self, pos): """ Set the position of the axis. pos hast to be one these constants: POSITION_AUTO, POSITION_BOTTOM, POSITION_LEFT, POSITION_RIGHT, POSITION_TOP. """ self.set_property("position", pos) self.emit("appearance_changed") def get_position(self): """ Returns the position of the axis. (see set_position for details). """ return self.get_property("position") def set_show_tics(self, show): """ Set whether to draw tics at the axis. @type show: boolean. """ self.set_property("show-tics", show) self.emit("appearance_changed") def get_show_tics(self): """ Returns True if tics are drawn. @return: boolean. """ return self.get_property("show-tics") def set_show_tic_labels(self, show): """ Set whether to draw tic labels. Labels are only drawn if tics are drawn. @type show: boolean. """ self.set_property("show-tic-labels", show) self.emit("appearance_changed") def get_show_tic_labels(self): """ Returns True if tic labels are shown. @return: boolean. """ return self.get_property("show-tic-labels") def set_tic_format_function(self, func): """ Use this to set the function that should be used to label the tics. The function should take a number as the only argument and return a string. Default: str @type func: function. """ self.set_property("tic-format-function", func) self.emit("appearance_changed") def get_tic_format_function(self): """ Returns the function currently used for labeling the tics. """ return self.get_property("tic-format-function") def set_logarithmic(self, log): """ Set whether the axis should use logarithmic (base 10) scale. @type log: boolean. """ self.set_property("logarithmic", log) self.emit("appearance_changed") def get_logarithmic(self): """ Returns True if the axis uses logarithmic scale. @return: boolean. """ return self.get_property("logarithmic") class XAxis(Axis): """ This class represents the xaxis. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The XAxis class inherits properties from Axis. Signals ======= The XAxis class inherits signals from Axis. """ def __init__(self, range_calc): Axis.__init__(self, range_calc, "x") def draw(self, context, rect, yaxis): """ This method is called by the parent Plot instance. It calls _do_draw. """ if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) self._do_draw(context, rect, yaxis) context.set_antialias(cairo.ANTIALIAS_DEFAULT) def _do_draw_tics(self, context, rect, yaxis): if self._show_tics: tics = self._range_calc.get_xtics(rect) #calculate yaxis position (zx, zy) = self._range_calc.get_absolute_zero(rect, self, yaxis) if yaxis.get_position() == POSITION_LEFT: zx = rect.width * GRAPH_PADDING elif yaxis.get_position() == POSITION_RIGHT: zx = rect.width * (1 - GRAPH_PADDING) for ((x,y), val) in tics: if self._position == POSITION_TOP: y = rect.height * GRAPH_PADDING elif self._position == POSITION_BOTTOM: y = rect.height * (1 - GRAPH_PADDING) tic_height = rect.height / 80.0 context.move_to(x, y + tic_height / 2) context.rel_line_to(0, - tic_height) context.stroke() if self._show_tic_labels: if abs(x - zx) < 10: #the distance to the yaxis is to small => do not draw label continue pos = x, y + tic_height text = self._tic_format_function(val) tic_label = label.Label(pos, text, anchor=label.ANCHOR_TOP_CENTER, fixed=True) tic_label.draw(context, rect) def _do_draw_label(self, context, rect, pos): axis_label = label.Label(pos, self._label, anchor=label.ANCHOR_LEFT_CENTER, fixed=True) axis_label.draw(context, rect) def _do_draw(self, context, rect, yaxis): """ Draw the axis. """ (zx, zy) = self._range_calc.get_absolute_zero(rect, self, yaxis) if self._position == POSITION_BOTTOM: zy = rect.height * (1 - GRAPH_PADDING) elif self._position == POSITION_TOP: zy = rect.height * GRAPH_PADDING if rect.height * GRAPH_PADDING <= zy and rect.height * (1 - GRAPH_PADDING) >= zy: context.set_source_rgb(0, 0, 0) #draw the line: context.move_to(rect.width * GRAPH_PADDING, zy) context.line_to(rect.width * (1 - GRAPH_PADDING), zy) context.stroke() #draw arrow: context.move_to(rect.width * (1 - GRAPH_PADDING) + 3, zy) context.rel_line_to(-3, -3) context.rel_line_to(0, 6) context.close_path() context.fill() if self._show_label: self._do_draw_label(context, rect, (rect.width * (1 - GRAPH_PADDING) + 3, zy)) self._do_draw_tics(context, rect, yaxis) class YAxis(Axis): """ This class represents the yaxis. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The YAxis class inherits properties from Axis. Signals ======= The YAxis class inherits signals from Axis. """ def __init__(self, range_calc): Axis.__init__(self, range_calc, "y") def draw(self, context, rect, xaxis): """ This method is called by the parent Plot instance. It calls _do_draw. """ if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) self._do_draw(context, rect, xaxis) context.set_antialias(cairo.ANTIALIAS_DEFAULT) def _do_draw_tics(self, context, rect, xaxis): if self._show_tics: tics = self._range_calc.get_ytics(rect) #calculate xaxis position (zx, zy) = self._range_calc.get_absolute_zero(rect, xaxis, self) if xaxis.get_position() == POSITION_BOTTOM: zy = rect.height * (1 - GRAPH_PADDING) elif xaxis.get_position() == POSITION_TOP: zy = rect.height * GRAPH_PADDING for ((x,y), val) in tics: if self._position == POSITION_LEFT: x = rect.width * GRAPH_PADDING elif self._position == POSITION_RIGHT: x = rect.width * (1 - GRAPH_PADDING) tic_width = rect.height / 80.0 context.move_to(x + tic_width / 2, y) context.rel_line_to(- tic_width, 0) context.stroke() if self._show_tic_labels: if abs(y - zy) < 10: #distance to xaxis is to small => do not draw label continue pos = x - tic_width, y text = self._tic_format_function(val) tic_label = label.Label(pos, text, anchor=label.ANCHOR_RIGHT_CENTER, fixed=True) tic_label.draw(context, rect) def _do_draw_label(self, context, rect, pos): axis_label = label.Label(pos, self._label, anchor=label.ANCHOR_BOTTOM_CENTER, fixed=True) axis_label.draw(context, rect) def _do_draw(self, context, rect, xaxis): (zx, zy) = self._range_calc.get_absolute_zero(rect, xaxis, self) if self._position == POSITION_LEFT: zx = rect.width * GRAPH_PADDING elif self._position == POSITION_RIGHT: zx = rect.width * (1 - GRAPH_PADDING) if rect.width * GRAPH_PADDING <= zx and rect.width * (1 - GRAPH_PADDING) >= zx: context.set_source_rgb(0, 0, 0) #draw line: context.move_to(zx, rect.height * (1 - GRAPH_PADDING)) context.line_to(zx, rect.height * GRAPH_PADDING) context.stroke() #draw arrow: context.move_to(zx, rect.height * GRAPH_PADDING - 3) context.rel_line_to(-3, 3) context.rel_line_to(6, 0) context.close_path() context.fill() if self._show_label: self._do_draw_label(context, rect, (zx, rect.height * GRAPH_PADDING - 3)) self._do_draw_tics(context, rect, xaxis) class Grid(ChartObject): """ A class representing the grid of the chart. It is used by the LineChart widget internally, there is no need to create an instance yourself. Properties ========== The Grid class inherits properties from chart_object.ChartObject. Additional properties: - show-horizontal (sets whther to show horizontal grid lines, type: boolean) - show-vertical (sets whther to show vertical grid lines, type: boolean) - color (the color of the grid lines, type: gtk.gdk.Color) - line-style-horizontal (the line style of the horizontal grid lines, type: a line style constant) - line-style-vertical (the line style of the vertical grid lines, type: a line style constant). Signals ======= The Grid class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"show-horizontal": (gobject.TYPE_BOOLEAN, "show horizontal lines", "Set whether to draw horizontal lines.", True, gobject.PARAM_READWRITE), "show-vertical": (gobject.TYPE_BOOLEAN, "show vertical lines", "Set whether to draw vertical lines.", True, gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "grid color", "The color of the grid in (r,g,b) format. r,g,b in [0,1]", gobject.PARAM_READWRITE), "line-style-horizontal": (gobject.TYPE_INT, "horizontal line style", "Line Style for the horizontal grid lines", 0, 3, 0, gobject.PARAM_READWRITE), "line-style-vertical": (gobject.TYPE_INT, "vertical line style", "Line Style for the vertical grid lines", 0, 3, 0, gobject.PARAM_READWRITE)} def __init__(self, range_calc): ChartObject.__init__(self) self.set_property("antialias", False) self._range_calc = range_calc self._color = gtk.gdk.color_parse("#DEDEDE") self._show_h = True self._show_v = True self._line_style_h = pygtk_chart.LINE_STYLE_SOLID self._line_style_v = pygtk_chart.LINE_STYLE_SOLID def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "show-horizontal": return self._show_h elif property.name == "show-vertical": return self._show_v elif property.name == "color": return self._color elif property.name == "line-style-horizontal": return self._line_style_h elif property.name == "line-style-vertical": return self._line_style_v else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "show-horizontal": self._show_h = value elif property.name == "show-vertical": self._show_v = value elif property.name == "color": self._color = value elif property.name == "line-style-horizontal": self._line_style_h = value elif property.name == "line-style-vertical": self._line_style_v = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, xaxis, yaxis): context.set_source_rgb(*color_gdk_to_cairo(self._color)) #draw horizontal lines if self._show_h: set_context_line_style(context, self._line_style_h) ytics = self._range_calc.get_ytics(rect) xa = rect.width * GRAPH_PADDING xb = rect.width * (1 - GRAPH_PADDING) for ((x, y), label) in ytics: context.move_to(xa, y) context.line_to(xb, y) context.stroke() context.set_dash([]) #draw vertical lines if self._show_v: set_context_line_style(context, self._line_style_v) xtics = self._range_calc.get_xtics(rect) ya = rect.height * GRAPH_PADDING yb = rect.height * (1 - GRAPH_PADDING) for ((x, y), label) in xtics: context.move_to(x, ya) context.line_to(x, yb) context.stroke() context.set_dash([]) def set_draw_horizontal_lines(self, draw): """ Set whether to draw horizontal grid lines. @type draw: boolean. """ self.set_property("show-horizontal", draw) self.emit("appearance_changed") def get_draw_horizontal_lines(self): """ Returns True if horizontal grid lines are drawn. @return: boolean. """ return self.get_property("show-horizontal") def set_draw_vertical_lines(self, draw): """ Set whether to draw vertical grid lines. @type draw: boolean. """ self.set_property("show-vertical", draw) self.emit("appearance_changed") def get_draw_vertical_lines(self): """ Returns True if vertical grid lines are drawn. @return: boolean. """ return self.get_property("show-vertical") def set_color(self, color): """ Set the color of the grid. @type color: gtk.gdk.Color @param color: The new color of the grid. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the color of the grid. @return: gtk.gdk.Color. """ return self.get_property("color") def set_line_style_horizontal(self, style): """ Set the line style of the horizontal grid lines. style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the constants above. """ self.set_property("line-style-horizontal", style) self.emit("appearance_changed") def get_line_style_horizontal(self): """ Returns ths current horizontal line style. @return: a line style constant. """ return self.get_property("line-style-horizontal") def set_line_style_vertical(self, style): """ Set the line style of the vertical grid lines. style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the constants above. """ self.set_property("line-style-vertical", style) self.emit("appearance_changed") def get_line_style_vertical(self): """ Returns ths current vertical line style. @return: a line style constant. """ return self.get_property("line-style-vertical") class Graph(ChartObject): """ This class represents a graph or the data you want to plot on your LineChart widget. Properties ========== The Graph class inherits properties from chart_object.ChartObject. Additional properties: - name (a unique id for the graph, type: string, read only) - title (the graph's title, type: string) - color (the graph's color, type: gtk.gdk.Color) - type (graph type, type: a graph type constant) - point-size (radius of the datapoints in px, type: int in [1, 100]) - fill-to (set how to fill space under the graph, type: None, Graph or float) - fill-color (the color of the filling, type: gtk.gdk.Color) - fill-opacity (the opacity of the filling, type: float in [0, 1]) - show-values (sets whether y values should be shown at the datapoints, type: boolean) - show-title (sets whether ot show the graph's title, type: boolean) - line-style (the graph's line style, type: a line style constant) - point-style (the graph's datapoints' point style, type: a point style constant) - clickable (sets whether datapoints are sensitive for clicks, type: boolean) - show-xerrors (sets whether x errors should be shown if error data is available, type: boolean) - show-yerrors (sets whether y errors should be shown if error data is available, type: boolean). Signals ======= The Graph class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"name": (gobject.TYPE_STRING, "graph id", "The graph's unique name.", "", gobject.PARAM_READABLE), "title": (gobject.TYPE_STRING, "graph title", "The title of the graph.", "", gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "graph color", "The color of the graph in (r,g,b) format. r,g,b in [0,1].", gobject.PARAM_READWRITE), "type": (gobject.TYPE_INT, "graph type", "The type of the graph.", 1, 3, 3, gobject.PARAM_READWRITE), "point-size": (gobject.TYPE_INT, "point size", "Radius of the data points.", 1, 100, 2, gobject.PARAM_READWRITE), "fill-to": (gobject.TYPE_PYOBJECT, "fill to", "Set how to fill space under the graph.", gobject.PARAM_READWRITE), "fill-color": (gobject.TYPE_PYOBJECT, "fill color", "Set which color to use when filling space under the graph.", gobject.PARAM_READWRITE), "fill-opacity": (gobject.TYPE_FLOAT, "fill opacity", "Set which opacity to use when filling space under the graph.", 0.0, 1.0, 0.3, gobject.PARAM_READWRITE), "show-values": (gobject.TYPE_BOOLEAN, "show values", "Sets whether to show the y values.", False, gobject.PARAM_READWRITE), "show-title": (gobject.TYPE_BOOLEAN, "show title", "Sets whether to show the graph's title.", True, gobject.PARAM_READWRITE), "line-style": (gobject.TYPE_INT, "line style", "The line style to use.", 0, 3, 0, gobject.PARAM_READWRITE), "point-style": (gobject.TYPE_PYOBJECT, "point style", "The graph's point style.", gobject.PARAM_READWRITE), "clickable": (gobject.TYPE_BOOLEAN, "clickable", "Sets whether datapoints should be clickable.", True, gobject.PARAM_READWRITE), "show-xerrors": (gobject.TYPE_BOOLEAN, "show xerrors", "Set whether to show x-errorbars.", True, gobject.PARAM_READWRITE), "show-yerrors": (gobject.TYPE_BOOLEAN, "show yerrors", "Set whether to show y-errorbars.", True, gobject.PARAM_READWRITE)} def __init__(self, name, title, data): """ Create a new graph instance. data should be a list of x,y pairs. If you want to provide error data for a datapoint, the tuple for that point has to be (x, y, xerror, yerror). If you want only one error, set the other to zero. You can mix datapoints with and without error data in data. @type name: string @param name: A unique name for the graph. This could be everything. It's just a name used internally for identification. You need to know this if you want to access or delete a graph from a chart. @type title: string @param title: The graphs title. This can be drawn on the chart. @type data: list (see above) @param data: This is the data you want to be visualized. For detail see description above. """ ChartObject.__init__(self) self._name = name self._title = title self._data, self._errors = separate_data_and_errors(data) self._color = COLOR_AUTO self._type = GRAPH_BOTH self._point_size = 2 self._show_value = False self._show_title = True self._fill_to = None self._fill_color = COLOR_AUTO self._fill_opacity = 0.3 self._line_style = pygtk_chart.LINE_STYLE_SOLID self._point_style = pygtk_chart.POINT_STYLE_CIRCLE self._clickable = True self._draw_xerrors = True self._draw_yerrors = True self._range_calc = None self._label = label.Label((0, 0), self._title, anchor=label.ANCHOR_LEFT_CENTER) def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "title": return self._title elif property.name == "color": return self._color elif property.name == "type": return self._type elif property.name == "point-size": return self._point_size elif property.name == "fill-to": return self._fill_to elif property.name == "fill-color": return self._fill_color elif property.name == "fill-opacity": return self._fill_opacity elif property.name == "show-values": return self._show_value elif property.name == "show-title": return self._show_title elif property.name == "line-style": return self._line_style elif property.name == "point-style": return self._point_style elif property.name == "clickable": return self._clickable elif property.name == "show-xerrors": return self._draw_xerrors elif property.name == "show-yerrors": return self._draw_yerrors else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "title": self._label.set_text(value) self._title = value elif property.name == "color": self._color = value elif property.name == "type": self._type = value elif property.name == "point-size": self._point_size = value elif property.name == "fill-to": self._fill_to = value elif property.name == "fill-color": self._fill_color = value elif property.name == "fill-opacity": self._fill_opacity = value elif property.name == "show-values": self._show_value = value elif property.name == "show-title": self._show_title = value elif property.name == "line-style": self._line_style = value elif property.name == "point-style": self._point_style = value elif property.name == "clickable": self._clickable = value elif property.name == "show-xerrors": self._draw_xerrors = value elif property.name == "show-yerrors": self._draw_yerrors = value else: raise AttributeError, "Property %s does not exist." % property.name def has_something_to_draw(self): return self._data != [] def _do_draw_lines(self, context, rect, xrange, yrange, xaxis, yaxis): context.set_source_rgb(*color_gdk_to_cairo(self._color)) set_context_line_style(context, self._line_style) first_point = None last_point = None for (x, y) in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if first_point == None: context.move_to(ax, ay) first_point = x, y else: context.line_to(ax, ay) last_point = ax, ay context.stroke() context.set_dash([]) return first_point, last_point def _do_draw_points(self, context, rect, xrange, yrange, xaxis, yaxis, highlighted_points): context.set_source_rgb(*color_gdk_to_cairo(self._color)) first_point = None last_point = None for (x, y) in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if self._clickable: chart.add_sensitive_area(chart.AREA_CIRCLE, (ax, ay, self._point_size), (x, y, self)) if first_point == None: context.move_to(ax, ay) #draw errors draw_errors(context, rect, self._range_calc, x, y, self._errors, self._draw_xerrors, self._draw_yerrors, xaxis, yaxis, self._point_size) #draw the point if type(self._point_style) != gtk.gdk.Pixbuf: draw_point(context, ax, ay, self._point_size, self._point_style) highlighted = (x, y, self) in highlighted_points if highlighted and self._clickable: context.set_source_rgba(1, 1, 1, 0.3) draw_point(context, ax, ay, self._point_size, self._point_style) context.set_source_rgb(*color_gdk_to_cairo(self._color)) else: draw_point_pixbuf(context, ax, ay, self._point_style) last_point = ax, ay return first_point, last_point def _do_draw_values(self, context, rect, xrange, yrange, xaxis, yaxis): anchors = {} first_point = True for i, (x, y) in enumerate(self._data): if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and is_in_range(y, yrange): next_point = None if i + 1 < len(self._data) and (is_in_range(self._data[i + 1][0], xrange) and is_in_range(self._data[i + 1][1], yrange)): next_point = self._data[i + 1] if first_point: if next_point != None: if next_point[1] >= y: anchors[(x, y)] = label.ANCHOR_TOP_LEFT else: anchors[(x, y)] = label.ANCHOR_BOTTOM_LEFT first_point = False else: previous_point = self._data[i - 1] if next_point != None: if previous_point[1] <= y <= next_point[1]: anchors[(x, y)] = label.ANCHOR_BOTTOM_RIGHT elif previous_point[1] > y > next_point[1]: anchors[(x, y)] = label.ANCHOR_BOTTOM_LEFT elif previous_point[1] < y and next_point[1] < y: anchors[(x, y)] = label.ANCHOR_BOTTOM_CENTER elif previous_point[1] > y and next_point[1] > y: anchors[(x, y)] = label.ANCHOR_TOP_CENTER else: if previous_point[1] >= y: anchors[(x, y)] = label.ANCHOR_TOP_RIGHT else: anchors[(x, y)] = label.ANCHOR_BOTTOM_RIGHT for x, y in self._data: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if (x, y) in anchors and is_in_range(x, xrange) and is_in_range(y, yrange): (ax, ay) = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) value_label = label.Label((ax, ay), str(y), anchor=anchors[(x, y)]) value_label.set_color(self._color) value_label.draw(context, rect) def _do_draw_title(self, context, rect, last_point, xaxis, yaxis): """ Draws the title. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. @type last_point: pairs of numbers @param last_point: The absolute position of the last drawn data point. """ if last_point: x = last_point[0] + 5 y = last_point[1] self._label.set_position((x, y)) self._label.set_color(self._color) self._label.draw(context, rect) def _do_draw_fill(self, context, rect, xrange, xaxis, yaxis): if type(self._fill_to) in (int, float): data = [] for i, (x, y) in enumerate(self._data): if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange) and not data: data.append((x, self._fill_to)) elif not is_in_range(x, xrange) and len(data) == 1: data.append((prev, self._fill_to)) break elif i == len(self._data) - 1: data.append((x, self._fill_to)) prev = x graph = Graph("none", "", data) elif type(self._fill_to) == Graph: graph = self._fill_to d = graph.get_data() range_b = d[0][0], d[-1][0] xrange = intersect_ranges(xrange, range_b) if not graph.get_visible(): return c = self._fill_color if c == COLOR_AUTO: c = self._color c = color_gdk_to_cairo(c) context.set_source_rgba(c[0], c[1], c[2], self._fill_opacity) data_a = self._data data_b = graph.get_data() first = True start_point = (0, 0) for x, y in data_a: if xaxis.get_logarithmic(): x = math.log10(x) if yaxis.get_logarithmic(): y = math.log10(y) if is_in_range(x, xrange): xa, ya = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if first: context.move_to(xa, ya) start_point = xa, ya first = False else: context.line_to(xa, ya) first = True for i in range(0, len(data_b)): j = len(data_b) - i - 1 x, y = data_b[j] xa, ya = self._range_calc.get_absolute_point(rect, x, y, xaxis, yaxis) if is_in_range(x, xrange): context.line_to(xa, ya) context.line_to(*start_point) context.fill() def _do_draw(self, context, rect, xaxis, yaxis, highlighted_points): """ Draw the graph. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ (xrange, yrange) = self._range_calc.get_ranges(xaxis, yaxis) if self._type in [GRAPH_LINES, GRAPH_BOTH]: first_point, last_point = self._do_draw_lines(context, rect, xrange, yrange, xaxis, yaxis) if self._type in [GRAPH_POINTS, GRAPH_BOTH]: first_point, last_point = self._do_draw_points(context, rect, xrange, yrange, xaxis, yaxis, highlighted_points) if self._fill_to != None: self._do_draw_fill(context, rect, xrange, xaxis, yaxis) if self._show_value and self._type in [GRAPH_POINTS, GRAPH_BOTH]: self._do_draw_values(context, rect, xrange, yrange, xaxis, yaxis) if self._show_title: self._do_draw_title(context, rect, last_point, xaxis, yaxis) def get_x_range(self): """ Get the the endpoints of the x interval. @return: pair of numbers """ try: self._data.sort(lambda x, y: cmp(x[0], y[0])) return (self._data[0][0], self._data[-1][0]) except: return None def get_y_range(self): """ Get the the endpoints of the y interval. @return: pair of numbers """ try: self._data.sort(lambda x, y: cmp(x[1], y[1])) return (self._data[0][1], self._data[-1][1]) except: return None def get_name(self): """ Get the name of the graph. @return: string """ return self.get_property("name") def get_title(self): """ Returns the title of the graph. @return: string """ return self.get_property("title") def set_title(self, title): """ Set the title of the graph. @type title: string @param title: The graph's new title. """ self.set_property("title", title) self.emit("appearance_changed") def set_range_calc(self, range_calc): self._range_calc = range_calc def get_color(self): """ Returns the current color of the graph or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("color") def set_color(self, color): """ Set the color of the graph. If set to COLOR_AUTO, the color will be choosen dynamicly. @type color: gtk.gdk.Color @param color: The new color of the graph. """ self.set_property("color", color) self.emit("appearance_changed") def get_type(self): """ Returns the type of the graph. @return: a type constant (see set_type() for details) """ return self.get_property("type") def set_type(self, type): """ Set the type of the graph to one of these: - GRAPH_POINTS: only show points - GRAPH_LINES: only draw lines - GRAPH_BOTH: draw points and lines, i.e. connect points with lines @param type: One of the constants above. """ self.set_property("type", type) self.emit("appearance_changed") def get_point_size(self): """ Returns the radius of the data points. @return: a poisitive integer """ return self.get_property("point_size") def set_point_size(self, size): """ Set the radius of the drawn points. @type size: a positive integer in [1, 100] @param size: The new radius of the points. """ self.set_property("point_size", size) self.emit("appearance_changed") def get_fill_to(self): """ The return value of this method depends on the filling under the graph. See set_fill_to() for details. """ return self.get_property("fill-to") def set_fill_to(self, fill_to): """ Use this method to specify how the space under the graph should be filled. fill_to has to be one of these: - None: dont't fill the space under the graph. - int or float: fill the space to the value specified (setting fill_to=0 means filling the space between graph and xaxis). - a Graph object: fill the space between this graph and the graph given as the argument. The color of the filling is the graph's color with 30% opacity. @type fill_to: one of the possibilities listed above. """ self.set_property("fill-to", fill_to) self.emit("appearance_changed") def get_fill_color(self): """ Returns the color that is used to fill space under the graph or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("fill-color") def set_fill_color(self, color): """ Set which color should be used when filling the space under a graph. If color is COLOR_AUTO, the graph's color will be used. @type color: gtk.gdk.Color or COLOR_AUTO. """ self.set_property("fill-color", color) self.emit("appearance_changed") def get_fill_opacity(self): """ Returns the opacity that is used to fill space under the graph. """ return self.get_property("fill-opacity") def set_fill_opacity(self, opacity): """ Set which opacity should be used when filling the space under a graph. The default is 0.3. @type opacity: float in [0, 1]. """ self.set_property("fill-opacity", opacity) self.emit("appearance_changed") def get_show_values(self): """ Returns True if y values are shown. @return: boolean """ return self.get_property("show-values") def set_show_values(self, show): """ Set whether the y values should be shown (only if graph type is GRAPH_POINTS or GRAPH_BOTH). @type show: boolean """ self.set_property("show-values", show) self.emit("appearance_changed") def get_show_title(self): """ Returns True if the title of the graph is shown. @return: boolean. """ return self.get_property("show-title") def set_show_title(self, show): """ Set whether to show the graph's title or not. @type show: boolean. """ self.set_property("show-title", show) self.emit("appearance_changed") def add_data(self, data_list): """ Add data to the graph. data_list should be a list of x,y pairs. If you want to provide error data for a datapoint, the tuple for that point has to be (x, y, xerror, yerror). If you want only one error, set the other to zero. You can mix datapoints with and without error data in data_list. @type data_list: a list (see above). """ new_data, new_errors = separate_data_and_errors(data_list) self._data += new_data self._errors = dict(self._errors, **new_errors) self._range_calc.add_graph(self) def get_data(self): """ Returns the data of the graph. @return: a list of x, y pairs. """ return self._data def set_line_style(self, style): """ Set the line style that should be used for drawing the graph (if type is line_chart.GRAPH_LINES or line_chart.GRAPH_BOTH). style has to be one of these constants: - pygtk_chart.LINE_STYLE_SOLID (default) - pygtk_chart.LINE_STYLE_DOTTED - pygtk_chart.LINE_STYLE_DASHED - pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC. @param style: the new line style @type style: one of the line style constants above. """ self.set_property("line-style", style) self.emit("appearance_changed") def get_line_style(self): """ Returns the current line style for the graph (see L{set_line_style} for details). @return: a line style constant. """ return self.get_property("line-style") def set_point_style(self, style): """ Set the point style that should be used when drawing the graph (if type is line_chart.GRAPH_POINTS or line_chart.GRAPH_BOTH). For style you can use one of these constants: - pygtk_chart.POINT_STYLE_CIRCLE (default) - pygtk_chart.POINT_STYLE_SQUARE - pygtk_chart.POINT_STYLE_CROSS - pygtk_chart.POINT_STYLE_TRIANGLE_UP - pygtk_chart.POINT_STYLE_TRIANGLE_DOWN - pygtk_chart.POINT_STYLE_DIAMOND style can also be a gtk.gdk.Pixbuf that should be used as point. @param style: the new point style @type style: one of the cosnatnts above or gtk.gdk.Pixbuf. """ self.set_property("point-style", style) self.emit("appearance_changed") def get_point_style(self): """ Returns the current point style. See L{set_point_style} for details. @return: a point style constant or gtk.gdk.Pixbuf. """ return self.get_property("point-style") def set_clickable(self, clickable): """ Set whether the datapoints of the graph should be clickable (only if the datapoints are shown). If this is set to True, the LineChart will emit the signal 'datapoint-clicked' when a datapoint was clicked. @type clickable: boolean. """ self.set_property("clickable", clickable) self.emit("appearance_changed") def get_clickable(self): """ Returns True if the datapoints of the graph are clickable. @return: boolean. """ return self.get_property("clickable") def set_show_xerrors(self, show): """ Use this method to set whether x-errorbars should be shown if error data is available. @type show: boolean. """ self.set_property("show-xerrors", show) self.emit("appearance_changed") def get_show_xerrors(self): """ Returns True if x-errorbars should be drawn if error data is available. @return: boolean. """ return self.get_property("show-xerrors") def set_show_yerrors(self, show): """ Use this method to set whether y-errorbars should be shown if error data is available. @type show: boolean. """ self.set_property("show-yerrors", show) self.emit("appearance_changed") def get_show_yerrors(self): """ Returns True if y-errorbars should be drawn if error data is available. @return: boolean. """ return self.get_property("show-yerrors") def graph_new_from_function(func, xmin, xmax, graph_name, samples=100, do_optimize_sampling=True): """ Returns a line_chart.Graph with data created from the function y = func(x) with x in [xmin, xmax]. The id of the new graph is graph_name. The parameter samples gives the number of points that should be evaluated in [xmin, xmax] (default: 100). If do_optimize_sampling is True (default) additional points will be evaluated to smoothen the curve. @type func: a function @param func: the function to evaluate @type xmin: float @param xmin: the minimum x value to evaluate @type xmax: float @param xmax: the maximum x value to evaluate @type graph_name: string @param graph_name: a unique name for the new graph @type samples: int @param samples: number of samples @type do_optimize_sampling: boolean @param do_optimize_sampling: set whether to add additional points @return: line_chart.Graph """ delta = (xmax - xmin) / float(samples - 1) data = [] x = xmin while x <= xmax: data.append((x, func(x))) x += delta if do_optimize_sampling: data = optimize_sampling(func, data) return Graph(graph_name, "", data) def optimize_sampling(func, data): new_data = [] prev_point = None prev_slope = None for x, y in data: if prev_point != None: if (x - prev_point[0]) == 0: return data slope = (y - prev_point[1]) / (x - prev_point[0]) if prev_slope != None: if abs(slope - prev_slope) >= 0.1: nx = prev_point[0] + (x - prev_point[0]) / 2.0 ny = func(nx) new_data.append((nx, ny)) #print abs(slope - prev_slope), prev_point[0], nx, x prev_slope = slope prev_point = x, y if new_data: data += new_data data.sort(lambda x, y: cmp(x[0], y[0])) return optimize_sampling(func, data) else: return data def graph_new_from_file(filename, graph_name, x_col=0, y_col=1, xerror_col=-1, yerror_col=-1): """ Returns a line_chart.Graph with point taken from data file filename. The id of the new graph is graph_name. Data file format: The columns in the file have to be separated by tabs or one or more spaces. Everything after '#' is ignored (comment). Use the parameters x_col and y_col to control which columns to use for plotting. By default, the first column (x_col=0) is used for x values, the second (y_col=1) is used for y values. The parameters xerror_col and yerror_col should point to the column in which the x/y error values are. If you do not want to provide x or y error data, omit the paramter or set it to -1 (default). @type filename: string @param filename: path to the data file @type graph_name: string @param graph_name: a unique name for the graph @type x_col: int @param x_col: the number of the column to use for x values @type y_col: int @param y_col: the number of the column to use for y values @type xerror_col: int @param xerror_col: index of the column for x error values @type yerror_col: int @param yerror_col: index of the column for y error values @return: line_chart.Graph """ points = [] f = open(filename, "r") data = f.read() f.close() lines = data.split("\n") for line in lines: line = line.strip() #remove special characters at beginning and end #remove comments: a = line.split("#", 1) if a and a[0]: line = a[0] #get data from line: if line.find("\t") != -1: #col separator is tab d = line.split("\t") else: #col separator is one or more space #normalize to one space: while line.find(" ") != -1: line = line.replace(" ", " ") d = line.split(" ") d = filter(lambda x: x, d) d = map(lambda x: float(x), d) new_data = (d[x_col], d[y_col]) if xerror_col != -1 or yerror_col != -1: xerror = 0 yerror = 0 if xerror_col != -1: xerror = d[xerror_col] if yerror_col != -1: yerror = d[yerror_col] new_data = (d[x_col], d[y_col], xerror, yerror) points.append(new_data) return Graph(graph_name, "", points) class Legend(ChartObject): """ This class represents a legend on a line chart. Properties ========== The Legend class inherits properties from chart_object.ChartObject. Additional properties: - position (the legend's position on the chart, type: a corner position constant). Signals ======= The Legend class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"position": (gobject.TYPE_INT, "legend position", "Position of the legend.", 8, 11, 8, gobject.PARAM_READWRITE)} def __init__(self): ChartObject.__init__(self) self._show = False self._position = POSITION_TOP_RIGHT def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "position": return self._position else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "position": self._position = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect, graphs): context.set_line_width(1) width = 0.2 * rect.width label_width = width - 12 - 20 x = rect.width - width y = 16 total_height = 0 total_width = 0 for id, graph in graphs.iteritems(): if not graph.get_visible(): continue graph_label = label.Label((x + (width - label_width), y), graph.get_title(), anchor=label.ANCHOR_TOP_LEFT) graph_label.set_max_width(label_width) rwidth, rheight = graph_label.get_calculated_dimensions(context, rect) total_height += rheight + 6 total_width = max(total_width, rwidth) total_width += 18 + 20 if self._position == POSITION_TOP_RIGHT: x = rect.width - total_width - 16 y = 16 elif self._position == POSITION_BOTTOM_RIGHT: x = rect.width - total_width - 16 y = rect.height - 16 - total_height elif self._position == POSITION_BOTTOM_LEFT: x = 16 y = rect.height - 16 - total_height elif self._position == POSITION_TOP_LEFT: x = 16 y = 16 context.set_antialias(cairo.ANTIALIAS_NONE) context.set_source_rgb(1, 1, 1) context.rectangle(x, y - 3, total_width, total_height) context.fill_preserve() context.set_source_rgb(0, 0, 0) context.stroke() context.set_antialias(cairo.ANTIALIAS_DEFAULT) for id, graph in graphs.iteritems(): if not graph.get_visible(): continue #draw the label graph_label = label.Label((x + (width - label_width), y), graph.get_title(), anchor=label.ANCHOR_TOP_LEFT) graph_label.set_max_width(label_width) graph_label.draw(context, rect) #draw line if graph.get_type() in [GRAPH_LINES, GRAPH_BOTH]: lines = graph_label.get_line_count() line_height = graph_label.get_real_dimensions()[1] / lines set_context_line_style(context, graph.get_line_style()) context.set_source_rgb(*color_gdk_to_cairo(graph.get_color())) context.move_to(x + 6, y + line_height / 2) context.rel_line_to(20, 0) context.stroke() #draw point if graph.get_type() in [GRAPH_POINTS, GRAPH_BOTH]: lines = graph_label.get_line_count() line_height = graph_label.get_real_dimensions()[1] / lines context.set_source_rgb(*color_gdk_to_cairo(graph.get_color())) if type(graph.get_point_style()) != gtk.gdk.Pixbuf: draw_point(context, x + 6 + 20, y + line_height / 2, graph.get_point_size(), graph.get_point_style()) else: draw_point_pixbuf(context, x + 6 + 20, y + line_height / 2, graph.get_point_style()) y += graph_label.get_real_dimensions()[1] + 6 def set_position(self, position): """ Set the position of the legend. position has to be one of these position constants: - line_chart.POSITION_TOP_RIGHT (default) - line_chart.POSITION_BOTTOM_RIGHT - line_chart.POSITION_BOTTOM_LEFT - line_chart.POSITION_TOP_LEFT @param position: the legend's position @type position: one of the constants above. """ self.set_property("position", position) self.emit("appearance_changed") def get_position(self): """ Returns the position of the legend. See L{set_position} for details. @return: a position constant. """ return self.get_property("position") openxenmanager/pygtk_chart/chart_object.py0000644000175000017500000001225511636446664017630 0ustar rrsrrs#!/usr/bin/env python # # chart_object.py # # Copyright 2009 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ This module contains the ChartObject class. Author: Sven Festersen (sven@sven-festersen.de) """ import cairo import gobject class ChartObject(gobject.GObject): """ This is the base class for all things that can be drawn on a chart widget. It emits the signal 'appearance-changed' when it needs to be redrawn. Properties ========== ChartObject inherits properties from gobject.GObject. Additional properties: - visible (sets whether the object should be visible, type: boolean) - antialias (sets whether the object should be antialiased, type: boolean). Signals ======= ChartObject inherits signals from gobject.GObject, Additional signals: - appearance-changed (emitted if the object needs to be redrawn). """ __gsignals__ = {"appearance-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])} __gproperties__ = {"visible": (gobject.TYPE_BOOLEAN, "visibilty of the object", "Set whether to draw the object or not.", True, gobject.PARAM_READWRITE), "antialias": (gobject.TYPE_BOOLEAN, "use antialiasing", "Set whether to use antialiasing when drawing the object.", True, gobject.PARAM_READWRITE)} def __init__(self): gobject.GObject.__init__(self) self._show = True self._antialias = True def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): """ A derived class should override this method. The drawing stuff should happen here. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ pass def draw(self, context, rect, *args): """ This method is called by the parent Chart instance. It calls _do_draw. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ res = None if self._show: if not self._antialias: context.set_antialias(cairo.ANTIALIAS_NONE) res = self._do_draw(context, rect, *args) context.set_antialias(cairo.ANTIALIAS_DEFAULT) return res def set_antialias(self, antialias): """ This method sets the antialiasing mode of the ChartObject. Antialiasing is enabled by default. @type antialias: boolean @param antialias: If False, antialiasing is disabled for this ChartObject. """ self.set_property("antialias", antialias) self.emit("appearance_changed") def get_antialias(self): """ Returns True if antialiasing is enabled for the object. @return: boolean. """ return self.get_property("antialias") def set_visible(self, visible): """ Use this method to set whether the ChartObject should be visible or not. @type visible: boolean @param visible: If False, the PlotObject won't be drawn. """ self.set_property("visible", visible) self.emit("appearance_changed") def get_visible(self): """ Returns True if the object is visble. @return: boolean. """ return self.get_property("visible") gobject.type_register(ChartObject) openxenmanager/pygtk_chart/basics.py0000644000175000017500000000767011636446664016452 0ustar rrsrrs#!/usr/bin/env python # # misc.py # # Copyright 2008 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ This module contains simple functions needed by all other modules. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gtk import os import pygtk_chart def is_in_range(x, (xmin, xmax)): """ Use this method to test whether M{xmin <= x <= xmax}. @type x: number @type xmin: number @type xmax: number """ return (xmin <= x and xmax >= x) def intersect_ranges(range_a, range_b): min_a, max_a = range_a min_b, max_b = range_b return max(min_a, min_b), min(max_a, max_b) def get_center(rect): """ Find the center point of a rectangle. @type rect: gtk.gdk.Rectangle @param rect: The rectangle. @return: A (x, y) tuple specifying the center point. """ return rect.width / 2, rect.height / 2 def color_gdk_to_cairo(color): """ Convert a gtk.gdk.Color to cairo color. @type color: gtk.gdk.Color @param color: the color to convert @return: a color in cairo format. """ return (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0) def color_cairo_to_gdk(r, g, b): return gtk.gdk.Color(int(65535 * r), int(65535 * g), int(65535 * b)) def color_rgb_to_cairo(color): """ Convert a 8 bit RGB value to cairo color. @type color: a triple of integers between 0 and 255 @param color: The color to convert. @return: A color in cairo format. """ return (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0) def color_html_to_cairo(color): """ Convert a html (hex) RGB value to cairo color. @type color: html color string @param color: The color to convert. @return: A color in cairo format. """ if color[0] == '#': color = color[1:] (r, g, b) = (int(color[:2], 16), int(color[2:4], 16), int(color[4:], 16)) return color_rgb_to_cairo((r, g, b)) def color_list_from_file(filename): """ Read a file with one html hex color per line and return a list of cairo colors. """ result = [] if os.path.exists(filename): f = open(filename, "r") for line in f.readlines(): line = line.strip() result.append(color_html_to_cairo(line)) return result def gdk_color_list_from_file(filename): """ Read a file with one html hex color per line and return a list of gdk colors. """ result = [] if os.path.exists(filename): f = open(filename, "r") for line in f.readlines(): line = line.strip() result.append(gtk.gdk.color_parse(line)) return result def set_context_line_style(context, style): """ The the line style for a context. """ if style == pygtk_chart.LINE_STYLE_SOLID: context.set_dash([]) elif style == pygtk_chart.LINE_STYLE_DASHED: context.set_dash([5]) elif style == pygtk_chart.LINE_STYLE_DASHED_ASYMMETRIC: context.set_dash([6, 6, 2, 6]) elif style == pygtk_chart.LINE_STYLE_DOTTED: context.set_dash([1]) openxenmanager/pygtk_chart/label.py0000644000175000017500000006423511636446664016265 0ustar rrsrrs#!/usr/bin/env python # # text.py # # Copyright 2009 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ Contains the Label class. Author: Sven Festersen (sven@sven-festersen.de) """ import cairo import gobject import gtk import math import pango import pygtk from pygtk_chart import basics from pygtk_chart.chart_object import ChartObject ANCHOR_BOTTOM_LEFT = 0 ANCHOR_TOP_LEFT = 1 ANCHOR_TOP_RIGHT = 2 ANCHOR_BOTTOM_RIGHT = 4 ANCHOR_CENTER = 5 ANCHOR_TOP_CENTER = 6 ANCHOR_BOTTOM_CENTER = 7 ANCHOR_LEFT_CENTER = 8 ANCHOR_RIGHT_CENTER = 9 UNDERLINE_NONE = pango.UNDERLINE_NONE UNDERLINE_SINGLE = pango.UNDERLINE_SINGLE UNDERLINE_DOUBLE = pango.UNDERLINE_DOUBLE UNDERLINE_LOW = pango.UNDERLINE_LOW STYLE_NORMAL = pango.STYLE_NORMAL STYLE_OBLIQUE = pango.STYLE_OBLIQUE STYLE_ITALIC = pango.STYLE_ITALIC WEIGHT_ULTRALIGHT = pango.WEIGHT_ULTRALIGHT WEIGHT_LIGHT = pango.WEIGHT_LIGHT WEIGHT_NORMAL = pango.WEIGHT_NORMAL WEIGHT_BOLD = pango.WEIGHT_BOLD WEIGHT_ULTRABOLD = pango.WEIGHT_ULTRABOLD WEIGHT_HEAVY = pango.WEIGHT_HEAVY DRAWING_INITIALIZED = False REGISTERED_LABELS = [] def begin_drawing(): global DRAWING_INITIALIZED DRAWING_INITIALIZED = True def finish_drawing(): global REGISTERED_LABELS global DRAWING_INITIALIZED REGISTERED_LABELS = [] DRAWING_INITIALIZED = False def register_label(label): if DRAWING_INITIALIZED: REGISTERED_LABELS.append(label) def get_registered_labels(): if DRAWING_INITIALIZED: return REGISTERED_LABELS return [] class Label(ChartObject): """ This class is used for drawing all the text on the chart widgets. It uses the pango layout engine. Properties ========== The Label class inherits properties from chart_object.ChartObject. Additional properties: - color (the label's color, type: gtk.gdk.Color) - text (text to display, type: string) - position (the label's position, type: pair of float) - anchor (the anchor that should be used to position the label, type: an anchor constant) - underline (sets the type of underline, type; an underline constant) - max-width (the maximum width of the label in px, type: int) - rotation (angle of rotation in degrees, type: int) - size (the size of the label's text in px, type: int) - slant (the font slant, type: a slant style constant) - weight (the font weight, type: a font weight constant) - fixed (sets whether the position of the label may be changed dynamicly or not, type: boolean) - wrap (sets whether the label's text should be wrapped if it's longer than max-width, type: boolean). Signals ======= The Label class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"color": (gobject.TYPE_PYOBJECT, "label color", "The color of the label (a gtk.gdkColor)", gobject.PARAM_READWRITE), "text": (gobject.TYPE_STRING, "label text", "The text to show on the label.", "", gobject.PARAM_READWRITE), "position": (gobject.TYPE_PYOBJECT, "label position", "A pair of x,y coordinates.", gobject.PARAM_READWRITE), "anchor": (gobject.TYPE_INT, "label anchor", "The anchor of the label.", 0, 9, 0, gobject.PARAM_READWRITE), "underline": (gobject.TYPE_PYOBJECT, "underline text", "Set whether to underline the text.", gobject.PARAM_READWRITE), "max-width": (gobject.TYPE_INT, "maximum width", "The maximum width of the label.", 1, 99999, 99999, gobject.PARAM_READWRITE), "rotation": (gobject.TYPE_INT, "rotation of the label", "The angle that the label should be rotated by in degrees.", 0, 360, 0, gobject.PARAM_READWRITE), "size": (gobject.TYPE_INT, "text size", "The size of the text.", 0, 1000, 8, gobject.PARAM_READWRITE), "slant": (gobject.TYPE_PYOBJECT, "font slant", "The font slant style.", gobject.PARAM_READWRITE), "weight": (gobject.TYPE_PYOBJECT, "font weight", "The font weight.", gobject.PARAM_READWRITE), "fixed": (gobject.TYPE_BOOLEAN, "fixed", "Set whether the position of the label should be forced.", False, gobject.PARAM_READWRITE), "wrap": (gobject.TYPE_BOOLEAN, "wrap text", "Set whether text should be wrapped.", False, gobject.PARAM_READWRITE)} def __init__(self, position, text, size=None, slant=pango.STYLE_NORMAL, weight=pango.WEIGHT_NORMAL, underline=pango.UNDERLINE_NONE, anchor=ANCHOR_BOTTOM_LEFT, max_width=99999, fixed=False): ChartObject.__init__(self) self._position = position self._text = text self._size = size self._slant = slant self._weight = weight self._underline = underline self._anchor = anchor self._rotation = 0 self._color = gtk.gdk.Color() self._max_width = max_width self._fixed = fixed self._wrap = True self._real_dimensions = (0, 0) self._real_position = (0, 0) self._line_count = 1 self._context = None self._layout = None def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "text": return self._text elif property.name == "color": return self._color elif property.name == "position": return self._position elif property.name == "anchor": return self._anchor elif property.name == "underline": return self._underline elif property.name == "max-width": return self._max_width elif property.name == "rotation": return self._rotation elif property.name == "size": return self._size elif property.name == "slant": return self._slant elif property.name == "weight": return self._weight elif property.name == "fixed": return self._fixed elif property.name == "wrap": return self._wrap else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "text": self._text = value elif property.name == "color": self._color = value elif property.name == "position": self._position = value elif property.name == "anchor": self._anchor = value elif property.name == "underline": self._underline = value elif property.name == "max-width": self._max_width = value elif property.name == "rotation": self._rotation = value elif property.name == "size": self._size = value elif property.name == "slant": self._slant = value elif property.name == "weight": self._weight = value elif property.name == "fixed": self._fixed = value elif property.name == "wrap": self._wrap = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): self._do_draw_label(context, rect) def _do_draw_label(self, context, rect): angle = 2 * math.pi * self._rotation / 360.0 if self._context == None: label = gtk.Label() self._context = label.create_pango_context() pango_context = self._context attrs = pango.AttrList() attrs.insert(pango.AttrWeight(self._weight, 0, len(self._text))) attrs.insert(pango.AttrStyle(self._slant, 0, len(self._text))) attrs.insert(pango.AttrUnderline(self._underline, 0, len(self._text))) if self._size != None: attrs.insert(pango.AttrSize(1000 * self._size, 0, len(self._text))) if self._layout == None: self._layout = pango.Layout(pango_context) layout = self._layout layout.set_text(self._text) layout.set_attributes(attrs) #find out where to draw the layout and calculate the maximum width width = rect.width if self._anchor in [ANCHOR_BOTTOM_LEFT, ANCHOR_TOP_LEFT, ANCHOR_LEFT_CENTER]: width = rect.width - self._position[0] elif self._anchor in [ANCHOR_BOTTOM_RIGHT, ANCHOR_TOP_RIGHT, ANCHOR_RIGHT_CENTER]: width = self._position[0] text_width, text_height = layout.get_pixel_size() width = width * math.cos(angle) width = min(width, self._max_width) if self._wrap: layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_width(int(1000 * width)) x, y = get_text_pos(layout, self._position, self._anchor, angle) if not self._fixed: #Find already drawn labels that would intersect with the current one #and adjust position to avoid intersection. text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) other_labels = get_registered_labels() this_rect = gtk.gdk.Rectangle(int(x), int(y), int(real_width), int(real_height)) for label in other_labels: label_rect = label.get_allocation() intersection = this_rect.intersect(label_rect) if intersection.width == 0 and intersection.height == 0: continue y_diff = 0 if label_rect.y <= y and label_rect.y + label_rect.height >= y: y_diff = y - label_rect.y + label_rect.height elif label_rect.y > y and label_rect.y < y + real_height: y_diff = label_rect.y - real_height - y y += y_diff #draw layout context.move_to(x, y) context.rotate(angle) context.set_source_rgb(*basics.color_gdk_to_cairo(self._color)) context.show_layout(layout) context.rotate(-angle) context.stroke() #calculate the real dimensions text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) self._real_dimensions = real_width, real_height self._real_position = x, y self._line_count = layout.get_line_count() register_label(self) def get_calculated_dimensions(self, context, rect): angle = 2 * math.pi * self._rotation / 360.0 if self._context == None: label = gtk.Label() self._context = label.create_pango_context() pango_context = self._context attrs = pango.AttrList() attrs.insert(pango.AttrWeight(self._weight, 0, len(self._text))) attrs.insert(pango.AttrStyle(self._slant, 0, len(self._text))) attrs.insert(pango.AttrUnderline(self._underline, 0, len(self._text))) if self._size != None: attrs.insert(pango.AttrSize(1000 * self._size, 0, len(self._text))) if self._layout == None: self._layout = pango.Layout(pango_context) layout = self._layout layout.set_text(self._text) layout.set_attributes(attrs) #find out where to draw the layout and calculate the maximum width width = rect.width if self._anchor in [ANCHOR_BOTTOM_LEFT, ANCHOR_TOP_LEFT, ANCHOR_LEFT_CENTER]: width = rect.width - self._position[0] elif self._anchor in [ANCHOR_BOTTOM_RIGHT, ANCHOR_TOP_RIGHT, ANCHOR_RIGHT_CENTER]: width = self._position[0] text_width, text_height = layout.get_pixel_size() width = width * math.cos(angle) width = min(width, self._max_width) if self._wrap: layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_width(int(1000 * width)) x, y = get_text_pos(layout, self._position, self._anchor, angle) if not self._fixed: #Find already drawn labels that would intersect with the current one #and adjust position to avoid intersection. text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) other_labels = get_registered_labels() this_rect = gtk.gdk.Rectangle(int(x), int(y), int(real_width), int(real_height)) for label in other_labels: label_rect = label.get_allocation() intersection = this_rect.intersect(label_rect) if intersection.width == 0 and intersection.height == 0: continue y_diff = 0 if label_rect.y <= y and label_rect.y + label_rect.height >= y: y_diff = y - label_rect.y + label_rect.height elif label_rect.y > y and label_rect.y < y + real_height: y_diff = label_rect.y - real_height - y y += y_diff #calculate the dimensions text_width, text_height = layout.get_pixel_size() real_width = abs(text_width * math.cos(angle)) + abs(text_height * math.sin(angle)) real_height = abs(text_height * math.cos(angle)) + abs(text_width * math.sin(angle)) return real_width, real_height def set_text(self, text): """ Use this method to set the text that should be displayed by the label. @param text: the text to display. @type text: string """ self.set_property("text", text) self.emit("appearance_changed") def get_text(self): """ Returns the text currently displayed. @return: string. """ return self.get_property("text") def set_color(self, color): """ Set the color of the label. color has to be a gtk.gdk.Color. @param color: the color of the label @type color: gtk.gdk.Color. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the current color of the label. @return: gtk.gdk.Color. """ return self.get_property("color") def set_position(self, pos): """ Set the position of the label. pos has to be a x,y pair of absolute pixel coordinates on the widget. The position is not the actual position but the position of the Label's anchor point (see L{set_anchor} for details). @param pos: new position of the label @type pos: pair of (x, y). """ self.set_property("position", pos) self.emit("appearance_changed") def get_position(self): """ Returns the current position of the label. @return: pair of (x, y). """ return self.get_property("position") def set_anchor(self, anchor): """ Set the anchor point of the label. The anchor point is the a point on the label's edge that has the position you set with set_position(). anchor has to be one of the following constants: - label.ANCHOR_BOTTOM_LEFT - label.ANCHOR_TOP_LEFT - label.ANCHOR_TOP_RIGHT - label.ANCHOR_BOTTOM_RIGHT - label.ANCHOR_CENTER - label.ANCHOR_TOP_CENTER - label.ANCHOR_BOTTOM_CENTER - label.ANCHOR_LEFT_CENTER - label.ANCHOR_RIGHT_CENTER The meaning of the constants is illustrated below::: ANCHOR_TOP_LEFT ANCHOR_TOP_CENTER ANCHOR_TOP_RIGHT * * * ##################### ANCHOR_LEFT_CENTER * # * # * ANCHOR_RIGHT_CENTER ##################### * * * ANCHOR_BOTTOM_LEFT ANCHOR_BOTTOM_CENTER ANCHOR_BOTTOM_RIGHT The point in the center is of course referred to by constant label.ANCHOR_CENTER. @param anchor: the anchor point of the label @type anchor: one of the constants described above. """ self.set_property("anchor", anchor) self.emit("appearance_changed") def get_anchor(self): """ Returns the current anchor point that's used to position the label. See L{set_anchor} for details. @return: one of the anchor constants described in L{set_anchor}. """ return self.get_property("anchor") def set_underline(self, underline): """ Set the underline style of the label. underline has to be one of the following constants: - label.UNDERLINE_NONE: do not underline the text - label.UNDERLINE_SINGLE: draw a single underline (the normal underline method) - label.UNDERLINE_DOUBLE: draw a double underline - label.UNDERLINE_LOW; draw a single low underline. @param underline: the underline style @type underline: one of the constants above. """ self.set_property("underline", underline) self.emit("appearance_changed") def get_underline(self): """ Returns the current underline style. See L{set_underline} for details. @return: an underline constant (see L{set_underline}). """ return self.get_property("underline") def set_max_width(self, width): """ Set the maximum width of the label in pixels. @param width: the maximum width @type width: integer. """ self.set_property("max-width", width) self.emit("appearance_changed") def get_max_width(self): """ Returns the maximum width of the label. @return: integer. """ return self.get_property("max-width") def set_rotation(self, angle): """ Use this method to set the rotation of the label in degrees. @param angle: the rotation angle @type angle: integer in [0, 360]. """ self.set_property("rotation", angle) self.emit("appearance_changed") def get_rotation(self): """ Returns the current rotation angle. @return: integer in [0, 360]. """ return self.get_property("rotation") def set_size(self, size): """ Set the size of the text in pixels. @param size: size of the text @type size: integer. """ self.set_property("size", size) self.emit("appearance_changed") def get_size(self): """ Returns the current size of the text in pixels. @return: integer. """ return self.get_property("size") def set_slant(self, slant): """ Set the font slant. slat has to be one of the following: - label.STYLE_NORMAL - label.STYLE_OBLIQUE - label.STYLE_ITALIC @param slant: the font slant style @type slant: one of the constants above. """ self.set_property("slant", slant) self.emit("appearance_changed") def get_slant(self): """ Returns the current font slant style. See L{set_slant} for details. @return: a slant style constant. """ return self.get_property("slant") def set_weight(self, weight): """ Set the font weight. weight has to be one of the following: - label.WEIGHT_ULTRALIGHT - label.WEIGHT_LIGHT - label.WEIGHT_NORMAL - label.WEIGHT_BOLD - label.WEIGHT_ULTRABOLD - label.WEIGHT_HEAVY @param weight: the font weight @type weight: one of the constants above. """ self.set_property("weight", weight) self.emit("appearance_changed") def get_weight(self): """ Returns the current font weight. See L{set_weight} for details. @return: a font weight constant. """ return self.get_property("weight") def set_fixed(self, fixed): """ Set whether the position of the label should be forced (fixed=True) or if it should be positioned avoiding intersection with other labels. @type fixed: boolean. """ self.set_property("fixed", fixed) self.emit("appearance_changed") def get_fixed(self): """ Returns True if the label's position is forced. @return: boolean """ return self.get_property("fixed") def set_wrap(self, wrap): """ Set whether too long text should be wrapped. @type wrap: boolean. """ self.set_property("wrap", wrap) self.emit("appearance_changed") def get_wrap(self): """ Returns True if too long text should be wrapped. @return: boolean. """ return self.get_property("wrap") def get_real_dimensions(self): """ This method returns a pair (width, height) with the dimensions the label was drawn with. Call this method I{after} drawing the label. @return: a (width, height) pair. """ return self._real_dimensions def get_real_position(self): """ Returns the position of the label where it was really drawn. @return: a (x, y) pair. """ return self._real_position def get_allocation(self): """ Returns an allocation rectangle. @return: gtk.gdk.Rectangle. """ x, y = self._real_position w, h = self._real_dimensions return gtk.gdk.Rectangle(int(x), int(y), int(w), int(h)) def get_line_count(self): """ Returns the number of lines. @return: int. """ return self._line_count def get_text_pos(layout, pos, anchor, angle): """ This function calculates the position of bottom left point of the layout respecting the given anchor point. @return: (x, y) pair """ text_width_n, text_height_n = layout.get_pixel_size() text_width = text_width_n * abs(math.cos(angle)) + text_height_n * abs(math.sin(angle)) text_height = text_height_n * abs(math.cos(angle)) + text_width_n * abs(math.sin(angle)) height_delta = text_height - text_height_n x, y = pos ref = (0, -text_height) if anchor == ANCHOR_TOP_LEFT: ref = (0, 0) elif anchor == ANCHOR_TOP_RIGHT: ref = (-text_width, height_delta) elif anchor == ANCHOR_BOTTOM_RIGHT: ref = (-text_width, -text_height) elif anchor == ANCHOR_CENTER: ref = (-text_width / 2, -text_height / 2) elif anchor == ANCHOR_TOP_CENTER: ref = (-text_width / 2, 0) elif anchor == ANCHOR_BOTTOM_CENTER: ref = (-text_width / 2, -text_height) elif anchor == ANCHOR_LEFT_CENTER: ref = (0, -text_height / 2) elif anchor == ANCHOR_RIGHT_CENTER: ref = (-text_width, -text_height / 2) x += ref[0] y += ref[1] return x, y openxenmanager/pygtk_chart/__init__.py0000644000175000017500000000340211636446664016732 0ustar rrsrrs#!/usr/bin/env python # # __init__.py # # Copyright 2008 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ This package contains four pygtk widgets for drawing simple charts: - line_chart.LineChart for line charts, - pie_chart.PieChart for pie charts, - bar_chart.BarChart for bar charts, - bar_chart.MultiBarChart for charts with groups of bars. """ __docformat__ = "epytext" __version__ = "beta" __author__ = "Sven Festersen, John Dickinson" __license__ = "GPL" __url__ = "http://notmyname.github.com/pygtkChart/" import os from pygtk_chart.basics import gdk_color_list_from_file COLOR_AUTO = 0 COLORS = gdk_color_list_from_file(os.sep.join([os.path.dirname(__file__), "data", "tango.color"])) #line style LINE_STYLE_SOLID = 0 LINE_STYLE_DOTTED = 1 LINE_STYLE_DASHED = 2 LINE_STYLE_DASHED_ASYMMETRIC = 3 #point styles POINT_STYLE_CIRCLE = 0 POINT_STYLE_SQUARE = 1 POINT_STYLE_CROSS = 2 POINT_STYLE_TRIANGLE_UP = 3 POINT_STYLE_TRIANGLE_DOWN = 4 POINT_STYLE_DIAMOND = 5 openxenmanager/pygtk_chart/chart.py0000644000175000017500000005025411636446664016303 0ustar rrsrrs#!/usr/bin/env python # # plot.py # # Copyright 2008 Sven Festersen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ Module Contents =============== This is the main module. It contains the base classes for chart widgets. - class Chart: base class for all chart widgets. - class Background: background of a chart widget. - class Title: title of a chart. Colors ------ All colors that pygtkChart uses are gtk.gdk.Colors as used by PyGTK. Author: Sven Festersen (sven@sven-festersen.de) """ __docformat__ = "epytext" import cairo import gobject import gtk import os import pango import pangocairo import pygtk from pygtk_chart.chart_object import ChartObject from pygtk_chart.basics import * from pygtk_chart import label COLOR_AUTO = 0 AREA_CIRCLE = 0 AREA_RECTANGLE = 1 CLICK_SENSITIVE_AREAS = [] def init_sensitive_areas(): global CLICK_SENSITIVE_AREAS CLICK_SENSITIVE_AREAS = [] def add_sensitive_area(type, coords, data): global CLICK_SENSITIVE_AREAS CLICK_SENSITIVE_AREAS.append((type, coords, data)) def get_sensitive_areas(x, y): res = [] global CLICK_SENSITIVE_AREAS for type, coords, data in CLICK_SENSITIVE_AREAS: if type == AREA_CIRCLE: ax, ay, radius = coords if (ax - x) ** 2 + (ay - y) ** 2 <= radius ** 2: res.append(data) elif type == AREA_RECTANGLE: ax, ay, width, height = coords if ax <= x <= ax + width and ay <= y <= ay + height: res.append(data) return res class Chart(gtk.DrawingArea): """ This is the base class for all chart widgets. Properties ========== The Chart class inherits properties from gtk.DrawingArea. Additional properties: - padding (the amount of free white space between the chart's content and its border in px, type: int in [0, 100]. Signals ======= The Chart class inherits signals from gtk.DrawingArea. """ __gproperties__ = {"padding": (gobject.TYPE_INT, "padding", "The chart's padding.", 0, 100, 16, gobject.PARAM_READWRITE)} def __init__(self): gtk.DrawingArea.__init__(self) #private properties: self._padding = 16 #objects needed for every chart: self.background = Background() self.background.connect("appearance-changed", self._cb_appearance_changed) self.title = Title() self.title.connect("appearance-changed", self._cb_appearance_changed) self.add_events(gtk.gdk.BUTTON_PRESS_MASK|gtk.gdk.SCROLL_MASK|gtk.gdk.POINTER_MOTION_MASK) self.connect("expose_event", self._cb_expose_event) self.connect("button_press_event", self._cb_button_pressed) self.connect("motion-notify-event", self._cb_motion_notify) def do_get_property(self, property): if property.name == "padding": return self._padding else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "padding": self._padding = value else: raise AttributeError, "Property %s does not exist." % property.name def _cb_appearance_changed(self, object): """ This method is called after the appearance of an object changed and forces a redraw. """ self.queue_draw() def _cb_button_pressed(self, widget, event): pass def _cb_motion_notify(self, widget, event): pass def _cb_expose_event(self, widget, event): """ This method is called when an instance of Chart receives the gtk expose_event. @type widget: gtk.Widget @param widget: The widget that received the event. @type event: gtk.Event @param event: The event. """ self.context = widget.window.cairo_create() self.context.rectangle(event.area.x, event.area.y, \ event.area.width, event.area.height) self.context.clip() self.draw(self.context) return False def draw_basics(self, context, rect): """ Draw basic things that every plot has (background, title, ...). @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ self.background.draw(context, rect) self.title.draw(context, rect) #calculate the rectangle that's available for drawing the chart title_height = self.title.get_real_dimensions()[1] rect_height = int(rect.height - 3 * self._padding - title_height) rect_width = int(rect.width - 2 * self._padding) rect_x = int(rect.x + self._padding) rect_y = int(rect.y + title_height + 2 * self._padding) return gtk.gdk.Rectangle(rect_x, rect_y, rect_width, rect_height) def draw(self, context): """ Draw the widget. This method is called automatically. Don't call it yourself. If you want to force a redrawing of the widget, call the queue_draw() method. @type context: cairo.Context @param context: The context to draw on. """ rect = self.get_allocation() rect = gtk.gdk.Rectangle(0, 0, rect.width, rect.height) #transform rect to context coordinates context.set_line_width(1) rect = self.draw_basics(context, rect) def export_svg(self, filename, size=None): """ Saves the contents of the widget to svg file. The size of the image will be the size of the widget. @type filename: string @param filename: The path to the file where you want the chart to be saved. @type size: tuple @param size: Optional parameter to give the desired height and width of the image. """ if size is None: rect = self.get_allocation() width = rect.width height = rect.height else: width, height = size old_alloc = self.get_allocation self.get_allocation = lambda: gtk.gdk.Rectangle(0, 0, width, height) surface = cairo.SVGSurface(filename, width, height) ctx = cairo.Context(surface) context = pangocairo.CairoContext(ctx) self.draw(context) surface.finish() if size is not None: self.get_allocation = old_alloc def export_png(self, filename, size=None): """ Saves the contents of the widget to png file. The size of the image will be the size of the widget. @type filename: string @param filename: The path to the file where you want the chart to be saved. @type size: tuple @param size: Optional parameter to give the desired height and width of the image. """ if size is None: rect = self.get_allocation() width = rect.width height = rect.height else: width, height = size old_alloc = self.get_allocation self.get_allocation = lambda: gtk.gdk.Rectangle(0, 0, width, height) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) ctx = cairo.Context(surface) context = pangocairo.CairoContext(ctx) self.set_size_request(width, height) self.draw(context) surface.write_to_png(filename) if size is not None: self.get_allocation = old_alloc def set_padding(self, padding): """ Set the chart's padding. @param padding: the padding in px @type padding: int in [0, 100] (default: 16). """ self.set_property("padding", padding) self.queue_draw() def get_padding(self): """ Returns the chart's padding. @return: int in [0, 100]. """ return self.get_property("padding") class Background(ChartObject): """ The background of a chart. Properties ========== This class inherits properties from chart_object.ChartObject. Additional properties: - color (the background color, type: gtk.gdk.Color) - gradient (the background gradient, type: a pair of gtk.gdk.Color) - image (path to the background image file, type: string) Signals ======= The Background class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"color": (gobject.TYPE_PYOBJECT, "background color", "The color of the backround.", gobject.PARAM_READWRITE), "gradient": (gobject.TYPE_PYOBJECT, "background gradient", "A background gardient. (first_color, second_color)", gobject.PARAM_READWRITE), "image": (gobject.TYPE_STRING, "background image file", "Path to the image file to use as background.", "", gobject.PARAM_READWRITE)} def __init__(self): ChartObject.__init__(self) self._color = gtk.gdk.color_parse("#ffffff") #the backgound is filled white by default self._gradient = None self._image = "" self._pixbuf = None def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "gradient": return self._gradient elif property.name == "color": return self._color elif property.name == "image": return self._image else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "gradient": self._gradient = value elif property.name == "color": self._color = value elif property.name == "image": self._image = value else: raise AttributeError, "Property %s does not exist." % property.name def _do_draw(self, context, rect): """ Do all the drawing stuff. @type context: cairo.Context @param context: The context to draw on. @type rect: gtk.gdk.Rectangle @param rect: A rectangle representing the charts area. """ if self._color != None: #set source color context.set_source_rgb(*color_gdk_to_cairo(self._color)) elif self._gradient != None: #set source gradient cs = color_gdk_to_cairo(self._gradient[0]) ce = color_gdk_to_cairo(self._gradient[1]) gradient = cairo.LinearGradient(0, 0, 0, rect.height) gradient.add_color_stop_rgb(0, cs[0], cs[1], cs[2]) gradient.add_color_stop_rgb(1, ce[0], ce[1], ce[2]) context.set_source(gradient) elif self._pixbuf: context.set_source_pixbuf(self._pixbuf, 0, 0) else: context.set_source_rgb(1, 1, 1) #fallback to white bg #create the background rectangle and fill it: context.rectangle(0, 0, rect.width, rect.height) context.fill() def set_color(self, color): """ The set_color() method can be used to change the color of the background. @type color: gtk.gdk.Color @param color: Set the background to be filles with this color. """ self.set_property("color", color) self.set_property("gradient", None) self.set_property("image", "") self.emit("appearance_changed") def get_color(self): """ Returns the background's color. @return: gtk.gdk.Color. """ return self.get_property("color") def set_gradient(self, color_start, color_end): """ Use set_gradient() to define a vertical gradient as the background. @type color_start: gtk.gdk.Color @param color_start: The starting (top) color of the gradient. @type color_end: gtk.gdk.Color @param color_end: The ending (bottom) color of the gradient. """ self.set_property("color", None) self.set_property("gradient", (color_start, color_end)) self.set_property("image", "") self.emit("appearance_changed") def get_gradient(self): """ Returns the gradient of the background or None. @return: A (gtk.gdk.Color, gtk.gdk.Color) tuple or None. """ return self.get_property("gradient") def set_image(self, filename): """ The set_image() method sets the background to be filled with an image. @type filename: string @param filename: Path to the file you want to use as background image. If the file does not exists, the background is set to white. """ try: self._pixbuf = gtk.gdk.pixbuf_new_from_file(filename) except: self._pixbuf = None self.set_property("color", None) self.set_property("gradient", None) self.set_property("image", filename) self.emit("appearance_changed") def get_image(self): return self.get_property("image") class Title(label.Label): """ The title of a chart. The title will be drawn centered at the top of the chart. Properties ========== The Title class inherits properties from label.Label. Signals ======= The Title class inherits signals from label.Label. """ def __init__(self, text=""): label.Label.__init__(self, (0, 0), text, weight=pango.WEIGHT_BOLD, anchor=label.ANCHOR_TOP_CENTER, fixed=True) def _do_draw(self, context, rect, top=-1): if top == -1: top = rect.height / 80 self._size = max(8, int(rect.height / 50.0)) self._position = rect.width / 2, top self._do_draw_label(context, rect) class Area(ChartObject): """ This is a base class for classes that represent areas, e.g. the pie_chart.PieArea class and the bar_chart.Bar class. Properties ========== The Area class inherits properties from chart_object.ChartObject. Additional properties: - name (a unique name for the area, type: string, read only) - value (the value of the area, type: float) - color (the area's color, type: gtk.gdk.Color) - label (a label for the area, type: string) - highlighted (set whether the area should be highlighted, type: boolean). Signals ======= The Area class inherits signals from chart_object.ChartObject. """ __gproperties__ = {"name": (gobject.TYPE_STRING, "area name", "A unique name for the area.", "", gobject.PARAM_READABLE), "value": (gobject.TYPE_FLOAT, "value", "The value.", 0.0, 9999999999.0, 0.0, gobject.PARAM_READWRITE), "color": (gobject.TYPE_PYOBJECT, "area color", "The color of the area.", gobject.PARAM_READWRITE), "label": (gobject.TYPE_STRING, "area label", "The label for the area.", "", gobject.PARAM_READWRITE), "highlighted": (gobject.TYPE_BOOLEAN, "area is higlighted", "Set whether the area should be higlighted.", False, gobject.PARAM_READWRITE)} def __init__(self, name, value, title=""): ChartObject.__init__(self) self._name = name self._value = value self._label = title self._color = COLOR_AUTO self._highlighted = False def do_get_property(self, property): if property.name == "visible": return self._show elif property.name == "antialias": return self._antialias elif property.name == "name": return self._name elif property.name == "value": return self._value elif property.name == "color": return self._color elif property.name == "label": return self._label elif property.name == "highlighted": return self._highlighted else: raise AttributeError, "Property %s does not exist." % property.name def do_set_property(self, property, value): if property.name == "visible": self._show = value elif property.name == "antialias": self._antialias = value elif property.name == "value": self._value = value elif property.name == "color": self._color = value elif property.name == "label": self._label = value elif property.name == "highlighted": self._highlighted = value else: raise AttributeError, "Property %s does not exist." % property.name def set_value(self, value): """ Set the value of the area. @type value: float. """ self.set_property("value", value) self.emit("appearance_changed") def get_value(self): """ Returns the current value of the area. @return: float. """ return self.get_property("value") def set_color(self, color): """ Set the color of the area. @type color: gtk.gdk.Color. """ self.set_property("color", color) self.emit("appearance_changed") def get_color(self): """ Returns the current color of the area or COLOR_AUTO. @return: gtk.gdk.Color or COLOR_AUTO. """ return self.get_property("color") def set_label(self, label): """ Set the label for the area. @param label: the new label @type label: string. """ self.set_property("label", label) self.emit("appearance_changed") def get_label(self): """ Returns the current label of the area. @return: string. """ return self.get_property("label") def set_highlighted(self, highlighted): """ Set whether the area should be highlighted. @type highlighted: boolean. """ self.set_property("highlighted", highlighted) self.emit("appearance_changed") def get_highlighted(self): """ Returns True if the area is currently highlighted. @return: boolean. """ return self.get_property("highlighted") openxenmanager/tunnel.py0000644000175000017500000001333011636446664014162 0ustar rrsrrsimport socket, select import sys from threading import Thread import traceback class Tunnel: def __init__(self, session, location): self.client_fd = None self.server_fd= None self.ref = location[location.find("/", 8):] self.session = session self.ip = location[8:location.find("/", 8)] self.halt = False self.translate = False self.key = None def listen(self, port=None): sock = socket.socket() sock.bind(("127.0.0.1", port)) sock.listen(1) self.client_fd, addr = sock.accept() self.server_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_fd.connect((self.ip, 80)) # self.server_fd.send("CONNECT /console?ref=%s&session_id=%s HTTP/1.1\r\n\r\n" % (self.ref, self.session)) self.server_fd.send("CONNECT %s&session_id=%s HTTP/1.1\r\n\r\n" % (self.ref, self.session)) data = self.server_fd.recv(17) data = self.server_fd.recv(24) data = self.server_fd.recv(35) data = self.server_fd.recv(2) self.server_fd.setblocking(0) Thread(target=self.read_from_server, args=()).start() try: codes = ["\x39", "\x02", "\x28", "\x04", "\x05", "\x06", "\x08", "\x28", #/* !"#$%&' */ "\x0a", "\x0b", "\x09", "\x0d", "\x33", "\x0c", "\x34", "\x35", #* ()*+,-./ */ "\x0b", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", #* 01234567 */ "\x09", "\x0a", "\x27", "\x27", "\x33", "\x0d", "\x34", "\x35", #* 89:;<=>? */ "\x03", "\x1e", "\x30", "\x2e", "\x20", "\x12", "\x21", "\x22", #* @ABCDEFG */ "\x23", "\x17", "\x24", "\x25", "\x26", "\x32", "\x31", "\x18", #* HIJKLMNO */ "\x19", "\x10", "\x13", "\x1f", "\x14", "\x16", "\x2f", "\x11", #* PQRSTUVW */ "\x2d", "\x15", "\x2c", "\x1a", "\x2b", "\x1b", "\x07", "\x0c", #* XYZ[\]^_ */ "\x29", "\x1e", "\x30", "\x2e", "\x20", "\x12", "\x21", "\x22", #* `abcdefg */ "\x23", "\x17", "\x24", "\x25", "\x26", "\x32", "\x31", "\x18", #* hijklmno */ "\x19", "\x10", "\x13", "\x1f", "\x14", "\x16", "\x2f", "\x11", #* pqrstuvw */ "\x2d", "\x15", "\x2c", "\x1a", "\x2b", "\x1b", "\x29" #* xyz{|}~ */ ] codes2 = ["\x0239", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x0c", "\x09", "\x0a", "\x1b", "\x1b", # 12 "\x33", "\x35", "\x34", "\x08", #//space", !"#$%&'()*+`-./ -> 3 "\x0b", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0a", #//0123456789 -> 10 #"\x0127", "\x27", "\x0133", "\x000d", "\x0134", "\x0135", "\x0103", #//:;<=>?@ "\x34", "\x33", "\x56", "\x0b", "\x56", "\x0c", "\x1f", #//:;<=>?@ -> 7 "\x11e", "\x130", "\x12e", "\x120", "\x112", "\x121", "\x122", "\x123", "\x117", "\x124", "\x125", "\x126", "\x132", "\x131", # 14 "\x118", "\x119", "\x110", "\x113", "\x11f", "\x114", "\x116", "\x12f", "\x111", "\x12d", "\x115", "\x12c", #//A-Z -> 12 "\x1a", "\x2b", "\x1b", "\x07", "\x35", "\x29", #//[\]^_` "\x1e", "\x30", "\x2e", "\x20", "\x12", "\x21", "\x22", "\x23", "\x17", "\x24", "\x25", "\x26", "\x32", "\x31", "\x18", "\x19", "\x10", \ "\x13", "\x1f", "\x14", "\x16", "\x2f", "\x11", "\x2d", "\x15", "\x2c", #a-z "\x1a", "\x2b", "\x1b", "\x29" #//{|}~ ] from struct import pack data = self.client_fd.recv(1024) while data and self.halt == False: if ord(data[0]) == 4 and self.translate: if ord(data[7]) > 32 and ord(data[7]) < 127 and ord(data[7]) not in range(80, 91): if self.key: data = "\xfe" + data[1:7] + chr(int(self.key,16)) else: data = "\xfe" + data[1:7] + codes[ord(data[7])-32] self.server_fd.send(data) data = self.client_fd.recv(1024) except: if self.halt == False: print "Unexpected error:", sys.exc_info() print traceback.print_exc() else: pass self.client_fd.close() def get_free_port(self): sock = socket.socket() sock.bind(("127.0.0.1", 0)) (host, port) = sock.getsockname() sock.close() return port def send_data(self, data): self.server_fd.send(data) def read_from_server(self): try: while self.halt == False: ready_to_read, ready_to_write, in_error = select.select([self.server_fd], [], []) if self.server_fd in ready_to_read: data = self.server_fd.recv(1024) if "XenServer Virtual Terminal" in data: self.translate = False data = data[:7] + "\x00" + data[8:] elif "+HVMXEN-" in data: self.translate = True data = data[:7] + "\x00" + data[8:] self.client_fd.send(data) except: if self.halt == False: print "Unexpected error:", sys.exc_info() print traceback.print_exc() else: pass self.server_fd.close() def close(self): try: self.halt = True self.client_fd.send("close\n") self.client_fd.send("close\n") self.server_fd.send("close\n") del self except: pass openxenmanager/window_addserver.py0000644000175000017500000004741411636446664016235 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # Copyright (C) 2011 Cheng Sun # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import xtea import oxm import re from oxcSERVER import * class oxcWindowAddServer: """ Class with functions to manage "add server" window """ def on_addserver_clicked(self, widget, data=None): """ Function called when you press "add server" button or image """ # Show the add server window self.builder.get_object("addserver").show_all() self.on_addserverhostname_changed(self.builder.get_object("addserverhostname")) self.on_addserverssl_toggled(self.builder.get_object("checksslconnection")) self.builder.get_object("addserverhostname").grab_focus() def on_addserverhostname_changed(self, widget, data=None): """ Function called when hostname/ip text field is changed """ connectAddServer = self.builder.get_object("connectAddServer") hostname = widget.get_active_text() if re.match("^[a-zA-Z0-9\-_.]+?$", hostname): # If is valid, enable the button connectAddServer.set_sensitive(True) else: # If is invalid, disable the button connectAddServer.set_sensitive(False) def on_addserverssl_toggled(self, widget, data=None): """ Function called when "SSL connection" checkbox is toggled """ connectPort = self.builder.get_object("addserverport") # set the default port number ports = ["80", "443"] # for unencrypted and encrypted respectively if (not connectPort.get_text() or connectPort.get_text() == ports[not widget.get_active()]): connectPort.set_text(ports[widget.get_active()]) def on_connectAddServer_clicked(self, widget, data=None): """ Function called when you press the "connect" button """ # Get host, username and password host = self.builder.get_object("addserverhostname").get_active_text() user = self.builder.get_object("addserverusername").get_text() password = self.builder.get_object("addserverpassword").get_text() # Call to "add_server" function with params # This function try connect to server and authenticate self.add_server(host, user, password) def on_cancelAddServer_clicked(self, widget, data=None): """ Function called when you press the "cancel" button on "add server" dialog """ self.builder.get_object("addserver").hide() def add_server(self, host, user, password, iter=None, ssl = None, port = None): """ Function used to connect to server """ self.builder.get_object("addserver").hide() # check that we are not already connected # FIXME: csun: should be better done when we have controllers found = [] def add_helper(model, path, iter): if self.treestore.get(iter, 3, 5) == ("host", host): found.append(self.treestore.get(iter, 1)[0]) return True return False self.treestore.foreach(add_helper) if len(found): # Show an alert dialog showing error self.show_error_dlg("'%s' is already connected as '%s'" % (host, found[0]), "Error") return #Show a dialog with a progress bar.. it should be do better self.builder.get_object("wprogressconnect").show() # Check if SSL connection is selected if ssl is None: ssl = self.builder.get_object("checksslconnection").get_active() else: self.builder.get_object("checksslconnection").set_active(ssl) if port is None: port = int(self.builder.get_object("addserverport").get_text()) else: self.builder.get_object("addserverport").set_text(port) # Create a new oxcSERVER object self.builder.get_object("lblprogessconnect").set_label("Connecting to %s..." % host) server = oxcSERVER(host,user,password, self, ssl, port) self.xc_servers[host] = server # connect the signal handlers server.connect("connect-success", oxm.idle(self.server_connect_success)) server.connect("connect-failure", oxm.idle(self.server_connect_failure)) server.connect("sync-progress", oxm.idle(self.server_sync_progress)) server.connect("sync-success", oxm.idle(self.server_sync_update_tree)) server.connect("sync-failure", oxm.idle(self.server_sync_failure)) # begin async connection server.connect_server_async() # begin UI animation Thread(target=self.update_connect_status, args=(server,)).start() def update_connect_status(self, server): """Animates the progress bar during connection. """ while server.connectThread.isAlive(): self.builder.get_object("progressconnect").pulse() server.connectThread.join(1) # TODO: what does this variable do? if self.selected_host == None: self.selected_host = server.host def server_connect_success(self, server): """Callback when a server connects successfully. We begin "synchronising", where the server object downloads data about the server, and then we query it to update our UI """ # Hide "add server" window self.builder.get_object("addserver").hide() # Append to historical host list on "add server" window self.builder.get_object("listaddserverhosts").append([server.host]) # Fill left tree and get all data (pool, vm, storage, template..) Thread(target=server.sync).start() # If we use a master password then save the password # Password is saved encrypted with XTEA encrypted_password = "" if self.password: x = xtea.crypt("X" * (16-len(self.password)) + self.password, server.password, self.iv) encrypted_password = x.encode("hex") self.config_hosts[server.host] = [server.user, encrypted_password, server.ssl] self.config['servers']['hosts'] = self.config_hosts # Save relation host/user/passwords to configuration self.config.write() def server_connect_failure(self, server, msg): """Method called if connection fails """ # Show add server dialog again self.builder.get_object("addserver").show() # And hide progress bar self.builder.get_object("wprogressconnect").hide() # Show an alert dialog showing error self.show_error_dlg("%s" % msg, "Error connecting") def server_sync_progress(self, server, msg): print "Server sync progress %s" % msg self.builder.get_object("progressconnect").pulse() self.builder.get_object("lblprogessconnect").set_text("Synchronizing...\n%s" % msg) def server_sync_finish(self, server): # Hide window progress self.builder.get_object("wprogressconnect").hide() # Setting again the modelfiter it will be refresh internal path/references self.treeview.set_model(self.modelfilter) self.treeview.expand_all() def server_sync_failure(self, server, msg): server.logout() self.show_error_dlg(msg) self.server_sync_finish(server) def server_sync_update_tree(self, server): """Method called when connection loading is finished """ try: self.server_sync_progress(server, "") # Remove the server from tree; it will be created again below # FIXME: csun: this won't be necessary when we have controllers def remove_helper(model, path, iter): if self.treestore.get(iter, 1, 3) == (server.hostname, "server"): self.treestore.remove(iter) return True return False self.treestore.foreach(remove_helper) # TODO: csun: clean this up poolroot = None hostroot = {} root = "" server.treestore = self.treestore server.default_sr = "" for pool in server.all_pools.keys(): server.default_sr = server.all_pools[pool]['default_SR'] if server.all_pools[pool]['name_label']: poolroot = self.treestore.append(self.treeroot, [gtk.gdk.pixbuf_new_from_file\ ("images/poolconnected_16.png"),\ server.all_pools[pool]['name_label'], pool, "pool", "Running", server.host, pool, ['newvm', 'newstorage', 'importvm', 'disconnect'], server.host]) if poolroot: relacion = {} for ref in server.all_hosts.keys(): relacion[str(server.all_hosts[ref]['name_label'] + "_" + ref)] = ref server.all_hosts_keys = [] rkeys = relacion.keys() rkeys.sort(key=str.lower) for ref in rkeys: server.all_hosts_keys.append(relacion[ref]) for h in server.all_hosts_keys: host_uuid = server.all_hosts[h]['uuid'] host = server.all_hosts[h]['name_label'] host_enabled = server.all_hosts[h]['enabled'] host_address = server.all_hosts[h]['address'] if host_enabled: hostroot[h] = self.treestore.append(poolroot, [gtk.gdk.pixbuf_new_from_file\ ("images/tree_connected_16.png"),\ host, host_uuid, "host", "Running", server.host, h,\ ['newvm', 'importvm', 'newstorage', 'clean_reboot', 'clean_shutdown', 'shutdown'], host_address]) else: hostroot[h] = self.treestore.append(poolroot, [gtk.gdk.pixbuf_new_from_file\ ("images/tree_disabled_16.png"),\ host, host_uuid, "host", "Disconnected", server.host, h, \ [], host_address]) root = poolroot else: host_uuid = server.all_hosts[server.all_hosts.keys()[0]]['uuid'] host = server.all_hosts[server.all_hosts.keys()[0]]['name_label'] host_address = server.all_hosts[server.all_hosts.keys()[0]]['address'] host_enabled = server.all_hosts[server.all_hosts.keys()[0]]['enabled'] if host_enabled: hostroot[server.all_hosts.keys()[0]] = self.treestore.append(self.treeroot, [gtk.gdk.pixbuf_new_from_file\ ("images/tree_connected_16.png"),\ host, host_uuid, "host", "Running", server.host, server.all_hosts.keys()[0], ['newvm', 'importvm', 'newstorage', 'clean_reboot', 'clean_shutdown', 'shutdown', 'disconnect'], host_address]) else: hostroot[server.all_hosts.keys()[0]] = self.treestore.append(self.treeroot, [gtk.gdk.pixbuf_new_from_file\ ("images/tree_disabled_16.png"),\ host, host_uuid, "host", "Running", server.host, server.all_hosts.keys()[0], ['newvm', 'importvm', 'newstorage', 'clean_reboot', 'clean_shutdown', 'shutdown', 'disconnect'], host_address]) root = hostroot[server.all_hosts.keys()[0]] server.hostname = host server.hostroot = hostroot server.poolroot = poolroot relacion = {} for ref in server.all_vms.keys(): relacion[str(server.all_vms[ref]['name_label'] + "_" + ref)] = ref server.all_vms_keys = [] rkeys = relacion.keys() rkeys.sort(key=str.lower) for ref in rkeys: server.all_vms_keys.insert(0,relacion[ref]) for vm in server.all_vms_keys: if not server.all_vms[vm]['is_a_template']: if not server.all_vms[vm]['is_control_domain']: server.add_vm_to_tree(vm) for operation in server.all_vms[vm]["current_operations"]: server.track_tasks[operation] = vm else: server.host_vm[server.all_vms[vm]['resident_on']] = [vm, server.all_vms[vm]['uuid']] # Get all storage record for sr in server.all_storage.keys(): if server.all_storage[sr]['name_label'] != "XenServer Tools": if len(server.all_storage[sr]['PBDs']) == 0: server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_detached_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) continue broken = False for pbd_ref in server.all_storage[sr]['PBDs']: if not server.all_pbd[pbd_ref]['currently_attached']: broken = True server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_broken_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) if not broken: if server.all_storage[sr]['shared']: if sr == server.default_sr: server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) else: server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) else: for pbd in server.all_storage[sr]['PBDs']: if sr == server.default_sr: if server.all_pbd[pbd]['host'] in hostroot: server.last_storage_iter = self.treestore.append(hostroot[server.all_pbd[pbd]['host']], [\ gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) else: server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) else: if server.all_pbd[pbd]['host'] in hostroot: server.last_storage_iter = self.treestore.append(hostroot[server.all_pbd[pbd]['host']], [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) else: server.last_storage_iter = self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"),\ server.all_storage[sr]['name_label'], server.all_storage[sr]['uuid'],\ "storage", None, server.host, sr, server.all_storage[sr]['allowed_operations'], None]) for tpl in server.all_vms_keys: if server.all_vms[tpl]['is_a_template'] and not server.all_vms[tpl]['is_a_snapshot']: if server.all_vms[tpl]['last_booted_record'] == "": self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/template_16.png"),\ server.all_vms[tpl]['name_label'], server.all_vms[tpl]['uuid'],\ "template", None, server.host, tpl, server.all_vms[tpl]['allowed_operations'], None]) else: tpl_affinity = server.all_vms[tpl]['affinity'] if tpl_affinity in hostroot: self.treestore.append(hostroot[tpl_affinity], [\ gtk.gdk.pixbuf_new_from_file("images/user_template_16.png"),\ server.all_vms[tpl]['name_label'], server.all_vms[tpl]['uuid'],\ "custom_template", None, server.host, tpl, server.all_vms[tpl]['allowed_operations'], None]) else: self.treestore.append(root, [\ gtk.gdk.pixbuf_new_from_file("images/user_template_16.png"),\ server.all_vms[tpl]['name_label'], server.all_vms[tpl]['uuid'],\ "custom_template", None, server.host, tpl, server.all_vms[tpl]['allowed_operations'], None]) self.treeview.expand_all() # Create a new thread it receives updates self.xc_servers[self.selected_host].thread_event_next() # Fill alerts list on "alerts" window self.xc_servers[self.selected_host].fill_alerts(self.listalerts) self.update_n_alerts() finally: self.server_sync_finish(server) openxenmanager/window_host_nics.py0000644000175000017500000002203211636446664016234 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python class oxcWindowHostNics: """ Class to manage "nics" tab on host """ # Host NIC tab def on_treehostnics_button_press_event(self, widget, event): """ Function called when you select a nic on "host nics" """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo # Get selected nic iter = self.builder.get_object("listhostnics").get_iter(path) ref = self.builder.get_object("listhostnics").get_value(iter, 8) nic = self.xc_servers[self.selected_host].all_pif[ref] # Check if already is on a bond if len(nic['bond_master_of']): # If is already is on a bond enable remove button self.builder.get_object("bthostnicremove").set_sensitive(True) else: # If is already is on a bond disable remove button self.builder.get_object("bthostnicremove").set_sensitive(False) def on_bthostnicreadd_clicked(self, widget, data=None): """ Function called when you press on "Add bond" button """ # Show "Add Bond" window self.builder.get_object("addbond").show() # Hide below frame showing nic information self.builder.get_object("framenic").hide() listavailnics = self.builder.get_object("listavailnics") listbondnics = self.builder.get_object("listbondnics") # Fill the possible nics to create bond self.xc_servers[self.selected_host].fill_available_nics(listavailnics, listbondnics) def on_bthostnicremove_clicked(self, widget, data=None): """ Function called when you press on "Delete bond" button """ # Show confirmation dialog self.builder.get_object("dialogdeletehostnic").show() def on_acceptdialogdeletehostnic_clicked(self, widget, data=None): """ Function called when you accept delete nic confirmation dialog """ listhostnics = self.builder.get_object("listhostnics") treehostnics = self.builder.get_object("treehostnics") # Get the selected NIC selection = treehostnics.get_selection() if selection.get_selected()[1] != None: iter = selection.get_selected()[1] ref = listhostnics.get_value(iter,8) # Call to delete nic function self.xc_servers[self.selected_host].delete_nic(ref, self.selected_ref) self.builder.get_object("dialogdeletehostnic").hide() def on_canceldialogdeletehostnic_clicked(self, widget, data=None): """ Function called when you cancel delete nic confirmation dialog """ self.builder.get_object("dialogdeletehostnic").hide() # Add Bond Window def on_treeavailnics_button_press_event(self, widget, event): """ Function called when you press on available nics to create a bond """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo listbondnics = self.builder.get_object("listbondnics") listavailnics = self.builder.get_object("listavailnics") # Get selected available NIC iter = self.builder.get_object("listavailnics").get_iter(path) ref = listavailnics.get_value(iter, 0) # Show all information for selected nic self.xc_servers[self.selected_host].fill_nic_info(ref) # Show nic information frame self.builder.get_object("framenic").show() if listbondnics.__len__() < 2: # If number of selected nics to create bond are less than 2, enable "Add >" button self.builder.get_object("btaddbondednic").set_sensitive( listavailnics.get_value(iter, 3)) else: # Else disable "add" button self.builder.get_object("btaddbondednic").set_sensitive(False) # Disable "< Remove" nic button self.builder.get_object("btrembondednic").set_sensitive(False) def on_btaddbondednic_clicked(self, widget, data=None): """ Function called when you press on "Add >" button to move from available to selected nic """ treeavailnics = self.builder.get_object("treeavailnics") listavailnics = self.builder.get_object("listavailnics") listbondnics = self.builder.get_object("listbondnics") # Get selected NIC selection = treeavailnics.get_selection() if selection.get_selected()[1] != None: iter = selection.get_selected()[1] # Append to "selected nics" tree listbondnics.append([listavailnics.get_value(iter,0), listavailnics.get_value(iter,1)]) # Remove from "available nics" tree listavailnics.remove(iter) # If selected nics tree has two elements then enable "accept" (create bond) button self.builder.get_object("btacceptaddbond").set_sensitive(listbondnics.__len__() == 2) def on_btrembondednic_clicked(self, widget, data=None): """ Function called when you press on "< Remove" button to move from available to selected nic """ treebondnics = self.builder.get_object("treebondnics") listavailnics = self.builder.get_object("listavailnics") listbondnics = self.builder.get_object("listbondnics") selection = treebondnics.get_selection() # Get selected NIC if selection.get_selected()[1] != None: iter = selection.get_selected()[1] ref = listbondnics.get_value(iter,0) name = listbondnics.get_value(iter,1) # Append to "available nics" tree listavailnics.append([ref, name, None,True]) # Remove from "selected nics" tree listbondnics.remove(iter) # Disable "accept" (create bond) button because available nics is not 2 self.builder.get_object("btacceptaddbond").set_sensitive(False) def on_treebondnics_button_press_event(self, widget, event): """ Function called when you select a bond on "bonded nics" (selected nics) to create a bond """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo listbondnics = self.builder.get_object("listbondnics") iter = listbondnics.get_iter(path) # Get selected NIC on "bonded nics" tree ref = listbondnics.get_value(iter, 0) # Fill NIC info self.xc_servers[self.selected_host].fill_nic_info(ref) # Show frame and disable "Add >" button and enable "< Remove Button" self.builder.get_object("framenic").show() self.builder.get_object("btaddbondednic").set_sensitive(False) self.builder.get_object("btrembondednic").set_sensitive(True) def on_btacceptaddbond_clicked(self, widget, data=None): """ Function called when you accept "create bond" window """ listbondnics = self.builder.get_object("listbondnics") # Get two selected nics on right tree iter = listbondnics.get_iter((0,0)) ref = listbondnics.get_value(iter, 0) name = listbondnics.get_value(iter, 1) iter = listbondnics.get_iter((1,0)) ref2 = listbondnics.get_value(iter, 0) name2 = listbondnics.get_value(iter, 1) # Get if bond will be automatically add to new servers auto = self.builder.get_object("checkautoaddbond").get_active() # Call to function to create a new bond self.xc_servers[self.selected_host].create_bond(ref, ref2, name, name2,auto) # Hide "new bond" window self.builder.get_object("addbond").hide() def on_btcanceladdbond_clicked(self, widget, data=None): """ Function called when you cancel "create bond" window """ self.builder.get_object("addbond").hide() openxenmanager/images_map/0000755000175000017500000000000011636446664014405 5ustar rrsrrsopenxenmanager/images_map/storage.png0000644000175000017500000000341611636446664016563 0ustar rrsrrs‰PNG  IHDRàw=øbKGDùC» pHYsHHFÉk> vpAgxL¥¦IDATHÇÕ–OlwÇ¿¿?3¿™ÙÙÝ™Ù;¶cl'NÒ¦jìC‘Hh$Co.AªÚ nœ9pàĉí‰^¸ @DN¨¢)%ÇCâ$`/ñþ‰½özwgçßÎÌïÇ%E%¾påIOzúêé}ô•žôðÿäEáÞ½{èt: ”2v’$%J©Y©TL˲´Oû”RÿÉ<Ï‹0 “ápEBˆàúõëc¥ÔIÀÛo¿…ås0ì'/ŠoOMMU|ß7³,ƒÁ€)¥Àc ”RpÎÁ“Œ±±®ëÉääähvvöö ßÿn’&ÐO4üüÝïáÒ×_9Øo¯T+e¼tõuü«ÑÀ¿AE(Ù6ÇA½V‡ïûð'|Ô<®çaaa㬠žì×Nik(ì±]Ü’ªºQjÛüdÏôŽÄA@´$&lT$VcÄ*Q#É£t˜Gd }«é¨Ù'wî5ôÔÔ(N–Ä.Ê×~5QrFO¥¢l6Y(oéŠRª(BF„ª(fªR*Æ3â¸JÿüüœZYöeF'åè!ö?ø–_^}%;8¸xie_¼|ÍV¿»yi:†iš°í2ªÕ \ÇçÙð\žçbùÌ„Ðåúú“8ttðO¼ŠþƒŸ!;uµ÷{SÇ©&RKÒ1ï´Û4 CR²Kp]t+Ó4åÈÈŠ¼Ÿäñ?ºi»Ÿ7n}2ÊLŸÜ¢sßú5æ\ óoÐ"z‹äQ¹$`qŒ­pØ×•Ì(% ŒQpMWŒ I˜–åÐâLi‘dV¤•¼ÛD8?,²4æFŒ±Òæææ—A‰sªÚC¢vv×’(xÃ)ÛÔ0-„’ìíE8× ë QÀ4,‹À¶4åT ufaŽœžž(µ›»ïäyó<ÏQÅY(õÓv?ó>j•ÔýavÅ1™Ï ®¸˵»[ãé“m”Ëxž‹²?š;‰IßA­îÇq13íc¦Æíûm&t CX‚×>jPýá!ƒ ¾y1Á×Wʈ¤‹ïÿbÚ1nÜ-ð•ùš±ürÝ!! „(J) i‘d!³åI#[^œ~Z5 ‚Q†(±üý~JeAÁ¹†öQˆÝVŽAn!‘¤Hd 7ô¼ùX7LÕJŽë æy¨×k¨y\×ÅÒÒ"òÚŸþü±Åy‘g`þêià¯û6FÒ­6°~¨A–¦²#;ÀkŸ 1É-Ü]ïÁQ’aœ+Š#ƒ®‚”›Ç2ÚîÒQóXmÝ~Mð¿ÜÙÄÚÕ˧µùïï¤*…DsÁì)<§ Su>*s«($AH ’„áYG‚ô ÈºÙ"s ËríòŠ ‚ø›_}ï$þ/ Vý>>|V‡@(-ùȳ¿}Bpm˜«æ \‡Bè0 %Ë$e»$\§*Î]rýºwqûñ#!Ë*à­g]#É­zo$ñx_afž`®Z`oØ[Š€äâ\"ÌÚÇ ¨y5ø¾˞ĄcÁ«y¨T*Rè<ÙÞÞnÿèwùââ"x?ˆlj™®ŽªÈ°š¸P‘mÿ¢ ˆbPU s_‚8»(“4Ý8èõ‚`Dz½¬Ùj¥¦iÆ„ Žããn·{¸µµÕ:<ú333ÕóçÏ¿J)µ•R Ðu®ë‚RŠn· s\C®dñ´ÙüÛ—×ÖÚ?~ï½ÿ髸7Ÿuý†âPÞi%tEXtcreate-date2009-09-28T11:27:59-04:00+\Ûý%tEXtmodify-date2009-05-20T12:28:38-04:00Ëî‘tEXtSoftwareAdobe ImageReadyqÉe<IEND®B`‚openxenmanager/images_map/server.png0000644000175000017500000000163011636446664016421 0ustar rrsrrs‰PNG  IHDRàw=øbKGDÿÿÿ ½§“ pHYsHHFÉk>8IDATHÇÅTÍN+7=ž?’f¢   -(Y„@tƒÄ²›Â”7àºá!²kת.aÁ ,,*%­ ªR2<3xì±ÝŽž†’{5,ªZ²Fþ<:Çç;ÇþãA>Qÿzooﻲ ¿ø€*ó¿×ëõþ ”ê$It’$:ŽcM)Õ”Rýüü¬£(ÒQéÉd¢Ã0Ô÷÷÷ª×ëÝÍsfÇã±'„ø ‘òA¨R yžC))%¤” „ Ùl’ñxœ•%°’$±ÌBk ­5”RàœCñjJ)±°°€$IdiišZaâé驨MŸzzV*!¦©*K`‚ pΡ”*¦6í àœ#MS§,8çdnnB0ÆÀ+@/FÕh4çœs»4T*ôûýB4~Á€÷ÔëuAðÊXÎ9ò<‡”²¨­¯¯#Ë2¨–!ø¢0²Ðjµçù›ô"Î9¤”FAý³?QJ¿Ƙ?áº.¤”E{!PJÁ¶mX–Ïó0™LÀÃÙÙYÐívÃZ­Fã8þùööö9{霜|;ðððÇqÐjµŠ¬›Ôh­_%i8Âó<ìîîºív;p]·Ñh4¾:::zžV‡agYÖ0æù¾Æ(¥È² ZëB¶mÛE’F£‚ €çy°, J)-„ø /!jÿwJé,..V…¸¾¾.úl¾Æä<Ï‘ç9²,C«ÕÂùù¹^ZZRFƒ¿¼¼¬øÀcáÁééé¶1úðððOÇqü­­­7æþ{Ç1Ò4¥ôGJé÷¥bªµ~sR³ž®Â<Ï`æ[d}‚€L™$ù¾×u‹ÚÇŒ4M@Ïš©À$æçç Ãûý>¶··Q­VEƒnnn`žö÷(€ã8X[[Ãòò2jµ®®®Ðétpyy Û¶Úí6vvv …¥ÀÊÊ çŸíÍÍM¬®®O¸¹¾ï£Ýn¿OA¥RÑÓà„t:ÄqŒn·[H)Q¯×Ñl6ß§àîîN3|f˜‹GAEÿ×ø{)-1aIEND®B`‚openxenmanager/images_map/network.png0000644000175000017500000000430411636446664016605 0ustar rrsrrs‰PNG  IHDR szzô‹IDATX…µ—klÕåÇ?Ïÿzz.mO[Nï-Ø–‹ª”‚#0ât1Û4C‰d/p/ÌÈŒQ’í…}Ñá…dKØ%‹.bÒMN‘1Bœ‚PǸè€Ã¥PÊé9½žžûÿ¾¶ì—ü_üó<ù}¿ÿÏïyþÏïÜj´uÊÀC@-Pç€~à/ôtn%¸á:!XW]^üÄ]³jå»[¨ªPR9;äB"M|$ëmßÛûׂi¿…à]wxÿ»¶NMÀK´Ïzæþö™¢ýÎjk‹ñ<‡¢ÈØŽ‡çy¦KïéqöîgÃ[ŸÆ²ëÛôtùï ´uÞ6£&¼ãù}³¥mV„†ê® ®ç"<M÷!K2®ëaÚ.žŠ,p]“ç’ü¢k±å“ÓÓ±ùFòâÕKÚê¿ÿËÇêkK¨­ à8.¶mâ:&@U‘ñé ’x¸®‡$ EfZYß[>K¹­¶ä±­Ñº$vEoÞ@[§oþ¬ªý¿¶ª¶È§¢«0<2B.›Ævl„¤ ª:²ðBÂq=ò{²€àÓ|ºÂ¼–¦é>²/Ѽ‰Ä®á›2P1ûÁŽ7Ö=ô°ªH|qè4…l’BÁBÕƒ(jEÓp=È,Ë"—7Èæ-<…|MÓP Ÿ®P0mæ6O“?ü¼oé°~÷+$v_¥%]çë#®˜óS×õøÙ‹[ŠÇrT_¶§bX¦é’É™ŒŽ%‰12ž!™cltËre Ixž÷嚟<ºp.ˆï_+÷"õÉH‰_ݼã8'@ó—aá'›·Èæ-r Ãt(B.B(AdÅO TNiYáp˜d2‰OSp\Ó²1-‡;o¯"¬ýZ÷/jZýA÷qÞ|÷Sò–ŸœU„$–í`Y.’Ÿ&£)²,eªHèšL‘>ùèšLr"Ã@lˆLÎÄ0l,ËaÁÌÊ…´uVÜØ@[gðtßèŒïÜ?ÏN‚VËþžS åÏ3‘6 øeBA•â€JI@%T) juJBU‘rŽ?‡¦ù8×g01NÁ°¹£©RË®”T®P{_{‹¸g~=Ë^–#GNÒåà1•9ÍTÕ!W°Èç3û Ç¥¦:Bc}š*áz`Z“¤jؼý þ“eK3––ÐT  ~*5¥¡é¬…$ æß5‡{«t¾ø;Œ\--ÓQEösqHB’dbÃ4ÖW‘7lÇöLË!o¸”U”38 'OÅØ½?EEe)@õTk +pq(O:k’Í[drkŸy’B&ÆÅø©L|Á"•1H¦ \H˜¤Òy²9“tÖdl¢@b$Ë™s£lÿè,G_$™öhnŠ04œÈOE v!žÁ§©x@M…‡ãzx®MßÙ>ÈÑÒ2Ñ Ó!àS'ë<0B IJlLÛÅ4mþ´¥—m!\RÇHRÎ娮.†³ Ni /–t|š.;Ž‹i:DÊ^øùzâÉ0ሆy"N‘.Ó70ªÈI0­Äâ¶õØŽK&[ ëχéþèF&ÆÂ¹sˆT•áÏyœèOœ¼Rðê?ab—kU,½×§†š,Ûôú¢QÔbÊ# d³&™¬É‚yÕX¦Môì©Tžß¢®®’¼aÓ{b€tÖ¥®ª˜;šTkˆÆ†ÆMbcÃÙ‰LþÇ$v¹7"Àx:ýZ‘.î7˜ˆÅÇ®v;™TŠò²5•~ðÝV$!xáW»ñ„`pÈáØÉ8Ãc<Ëe÷ž³ùlB! IÏ KÅT„uöÝDO‡5U 6E/\èŸÝ8½¡ý®9œé¢¦¾–ãQ•‰Tž‹‰<½Ñv~ršCÿÂó@Hð#£ø}Ë—Ì ¦:D( ´,Ì}í ·ˆ·wv—®ûêa”ØåM„˜Q3í‰e‹æ »#ÌliF’TUÁï×1L‡þXšÒ?áÒeáåe!f5Gû©ª sÏüF–.¬CR}|qì"ï}|øUz:º®•»èéØñ©úÒo몂kÌnA÷©,l«el¢Œ|ÞÂï“h¨+öíIdIfÉ‚ª#ºÿ>€ë dU&“,ðÆŽýGç®'õÕÓðE<½íó£;rŽ ¼ÔGIHOBH29*Ê‚x’Ž+4„¬#•lÁeÿ±Q‚~Ö™a29“—»vÅR™Â2z:²×Ó™²%«yà7²Ï§¾¼¸µñ¹Ϥ¥¡„s±4%A ]•I¦ \ÏC×ä/ ÓöP‰öœæÝ?Ëd‡“Ÿ<›¸‘ÆMuÅóø‡Á"íÕR­´©,è#•µhi(áÎÙDÏOP3-@i±ÊØ„ÉàxŠCgb™èùáÎBÁúuß–§Œ©rßt[¾âéMBÓ”•®)=Ô´oä²®b™.ž;Ù†Iž¥™Ÿ[ŽýzÁ°º>{õqëk“ÞŠ+cÕú¥êô¾·—._ñÈ{ïoaùÒöÛ£E‹Þyþ[ö­æºþ.˜"Ö®]ûF‰µ¯:m¥gN Jc¦†šçŠÄιëÖÑ××÷û7¾s³ùÀ’%K|«W¯>___/©ª*Éò»uMÓÂóæÍ®ë²sçNz{{Y¹r%µµµlܸÑjmmðûý²“ ÃpmÛ¾|SÚ¶mÛß6lØðø%Úž={\]×#kÖ¬AQn ¦««‹d2‰®ëô÷÷sæÌÖ¯_y<›ÍªÑh´BÁªU«¤É~êÔ):tyÞàà`þS·²²R”——twwO‰Ì²¬ð¾}û.#š>}úUãñxÜhjjJìÝ»÷ª±¦¦¦+— ìÝ»7´ú”êÀÊ•+ßliiQ/½_B àyžØºuëG›7o~åëòñ›˜óÿ¨•Ó:'$‹IEND®B`‚openxenmanager/oxcSERVER_host.py0000644000175000017500000003673111636446664015444 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header from oxcSERVER_host_nics import * from oxcSERVER_host_network import * class oxcSERVERhost(oxcSERVERhostnics, oxcSERVERhostnetwork): def upload_patch(self, ref, filename): import httplib, os task_uuid = self.connection.task.create(self.session_uuid, "Uploading Patch", "Uploading Patch %s " % (filename)) self.track_tasks[task_uuid['Value']] = "Upload.Patch" size=os.stat(filename)[6] url = self.wine.selected_ip conn = httplib.HTTPS(url) conn.putrequest('PUT', '/pool_patch_upload?session_id=%s&task_id=%s' % (self.session_uuid, task_uuid['Value'])) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', str(size)) conn.endheaders() fp=open(filename, 'rb') blocknum=0 uploaded=0 blocksize=4096 while not self.halt_import: bodypart=fp.read(blocksize) blocknum+=1 if blocknum % 10 == 0: uploaded+=len(bodypart) if not bodypart: break conn.send(bodypart) fp.close() print "Finish upload.." def remove_patch(self, ref, patch): res = self.connection.Async.pool_patch.destroy(self.session_uuid, patch) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def apply_patch(self, ref, patch): res = self.connection.Async.pool_patch.apply(self.session_uuid, ref, patch) if "Value" in res: self.track_tasks[res['Value']] = ref else: print res def reconfigure_pif(self, pif_ref, conf_mode, ip, mask, gw, dns, ref): res = self.connection.PIF.reconfigure_ip(self.session_uuid, pif_ref, conf_mode, ip, mask, gw, dns) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] else: print res def change_server_password(self, old, new): self.connection.session.change_password(self.session_uuid, old, new) self.password = new def install_license_key(self, ref, filename): encoded = open(filename, "rb").read().encode("base64").replace("\n","") res = self.connection.host.license_apply(self.session_uuid, ref, encoded) if "Value" in res: print res self.track_tasks[res['Value']] = self.host_vm[ref][0] else: self.wine.builder.get_object("warninglicense").show() def join_pool(self, host, user, password): res = self.connection.pool.join(self.session_uuid, host, user, password) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[self.all_hosts.keys()[0]][0] else: self.wine.push_alert("%s: %s" % (res["ErrorDescription"][0], res["ErrorDescription"][1])) def fill_vms_which_prevent_evacuation(self, ref, list): list.clear() vms = self.connection.host.get_vms_which_prevent_evacuation(self.session_uuid, ref)["Value"] for vm in vms.keys(): # vms[vm][0] list.append([gtk.gdk.pixbuf_new_from_file("images/tree_running_16.png"), self.all_vms[vm]['name_label'], \ "Suspend or shutdown VM"]) def enter_maintancemode(self, ref): res = self.connection.Async.host.evacuate(self.session_uuid, ref) if "Value" in res: self.track_tasks[res['Value']] = self.host_vm[ref][0] self.connection.host.disable(self.session_uuid, ref) self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_MIGRATED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_HALTED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_SUSPENDED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE") self.connection.host.add_to_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_MIGRATED", "") self.connection.host.add_to_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_HALTED", "") self.connection.host.add_to_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_SUSPENDED", "") self.connection.host.add_to_other_config(self.session_uuid, ref, "MAINTENANCE_MODE", True) else: print res def exit_maintancemode(self, ref): self.connection.host.enable(self.session_uuid, ref) self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_MIGRATED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_HALTED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE_EVACUATED_VMS_SUSPENDED") self.connection.host.remove_from_other_config(self.session_uuid, ref, "MAINTENANCE_MODE") def thread_restore_server(self, ref, filename, name): Thread(target=self.restore_server, args=(ref, filename, name)).start() def thread_backup_server(self, ref, filename, name): Thread(target=self.backup_server, args=(ref, filename, name)).start() def thread_host_download_logs(self, ref, filename, name): Thread(target=self.host_download_logs, args=(ref, filename, name)).start() def create_pool(self, name, desc): pool_ref = self.all_pools.keys()[0] res = self.connection.pool.set_name_label(self.session_uuid, pool_ref, name) res = self.connection.pool.set_name_description(self.session_uuid, pool_ref, desc) if "Value" in res: self.track_tasks[res['Value']] = pool_ref else: print res def get_external_auth(self, ref): if "external_auth_type" in self.all_hosts[ref]: return [self.all_hosts[ref]['external_auth_type'], self.all_hosts[ref]['external_auth_service_name'], \ self.all_hosts[ref]['external_auth_configuration']] else: return ["", "", ""] def fill_domain_users(self, ref, listusers): users_logged = self.connection.session.get_all_subject_identifiers(self.session_uuid)['Value'] users = {} if self.all_hosts[ref]['external_auth_type']: listusers.append(("000", "", "-", "Local root account\n(Always granted access)")) for user in self.all_subject: users[self.all_subject[user]['subject_identifier']] = self.all_subject[user]['other_config'] roles = [] for role in self.all_subject[user]['roles']: roles.append(self.all_role[role]['name_label']) users[self.all_subject[user]['subject_identifier']]['roles'] = roles users[self.all_subject[user]['subject_identifier']]['ref'] = user if self.all_subject[user]['subject_identifier'] in users_logged: logged = "Yes" else: logged = "No" listusers.append((user, " ".join(roles), logged, self.all_subject[user]['other_config']['subject-gecos'] + "\n" + self.all_subject[user]['other_config']['subject-name'])) def has_hardware_script(self, ref): error = self.connection.host.call_plugin(self.session_uuid, ref, "dmidecode", "test", {}) return "XENAPI_MISSING_PLUGIN" not in error['ErrorDescription'] def fill_host_hardware(self, ref): hwinfo = self.connection.host.call_plugin(self.session_uuid, ref, "dmidecode", "main", {})['Value'] relation = {} keys = [] for line in hwinfo.split("\n"): if not line: continue if line[0] != "\t" and line not in relation: relation[line] = [] key = line keys.append(key) else: if line[0] == "\t": relation[key].append(line) else: relation[key].append("\n") for ch in self.wine.builder.get_object("hosttablehw").get_children(): self.wine.builder.get_object("hosttablehw").remove(ch) for key in keys: self.add_box_hardware(key, relation[key]) def add_box_hardware(self, title, text): vboxframe = gtk.Frame() vboxframe.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) vboxchild = gtk.Fixed() vboxevent = gtk.EventBox() vboxevent.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) vboxevent.add(vboxchild) vboxframe.add(vboxevent) vboxchildlabel1 = gtk.Label() vboxchildlabel1.set_selectable(True) vboxchildlabel1.set_markup("" + title + "") vboxchild.put(vboxchildlabel1, 5, 5) vboxchildlabel2 = gtk.Label() vboxchildlabel2.set_selectable(True) vboxchildlabel2.set_text('\n'.join(text) + "\n") vboxchild.put(vboxchildlabel2, 5, 35) self.wine.builder.get_object("hosttablehw").add(vboxframe) self.wine.builder.get_object("hosttablehw").show_all() def fill_host_network(self, ref, list): list.clear() for network_key in self.all_network.keys(): network = self.all_network[network_key] #for pif in network['PIFs']: # if self.all_pif[pif]['host'] == ref: # on_host = True if network['bridge'] != "xapi0": name = network['name_label'].replace('Pool-wide network associated with eth','Network ') desc = network['name_description'] auto = "no" if "automatic" in network['other_config']: if network['other_config']['automatic'] == "true": auto = "yes" else: auto = "No" pifs = filter(lambda lista: lista["network"] == network_key, self.all_pif.values()) vlan = "-" linkstatus = "-" macaddress = "-" nic = "-" for pif in pifs: if pif['host'] == ref: nic = "NIC " + pif['device'][-1:] if pif: if pif['VLAN'] != "-1": vlan = pif['VLAN'] if pif['metrics'] in self.all_pif_metrics and self.all_pif_metrics[pif['metrics']]['carrier']: # Link status linkstatus = "Connected" if pif['MAC'] != "fe:ff:ff:ff:ff:ff": macaddress = pif['MAC'] if macaddress != "-" and linkstatus == "-": linkstatus = "Disconnected" # FIXME: not bond networks list.append((name, desc, nic, vlan, auto, linkstatus, macaddress,network_key)) def fill_host_nics(self, ref, list): list.clear() for pif_key in self.all_pif.keys(): if self.all_pif[pif_key]['host'] == ref: if self.all_pif[pif_key]['metrics'] != "OpaqueRef:NULL": if self.all_pif[pif_key]['metrics'] not in self.all_pif_metrics: continue pif_metric = self.all_pif_metrics[self.all_pif[pif_key]['metrics']] pif = self.all_pif[pif_key] #pif = filter(lambda lista: lista["metrics"] == pif_key, self.all_pif.values()) if pif_metric['duplex']: duplex = "full" else: duplex = "half" if pif: if "MAC" in pif: mac = pif['MAC'] else: mac = "" connected = "Disconnected" if pif_metric['carrier']: connected = "Connected" if connected == "Connected": speed = pif_metric['speed'] + " mbit/s" else: speed = "" if pif_metric['pci_bus_path'] != "N/A": list.append(("NIC %s" % pif['device'][-1:],mac,connected, speed, duplex, pif_metric['vendor_name'], pif_metric['device_name'], pif_metric['pci_bus_path'],pif_key)) else: if pif['bond_master_of']: devices = [] for slave in self.all_bond[pif['bond_master_of'][0]]['slaves']: devices.append(self.all_pif[slave]['device'][-1:]) devices.sort() list.append(("Bond %s" % ('+'.join(devices)),mac,connected, speed, duplex, pif_metric['vendor_name'], pif_metric['device_name'], pif_metric['pci_bus_path'],pif_key)) else: pass #print pif, pif_metric else: pif = self.all_pif[pif_key] if "MAC" in pif: mac = pif['MAC'] else: mac = "" connected = "Disconnected" if pif['bond_master_of']: devices = [] if pif['bond_master_of'][0] in self.all_bond: for slave in self.all_bond[pif['bond_master_of'][0]]['slaves']: devices.append(self.all_pif[slave]['device'][-1:]) devices.sort() list.append(("Bond %s" % ('+'.join(devices)),mac,connected, "-", "-", "-", "-", "-",pif_key)) else: list.append(("NIC %s" % pif['device'][-1:],mac,connected, "-", "-", "-", "-", "-",pif_key)) openxenmanager/oxcSERVER_addserver.py0000644000175000017500000002217711636446664016445 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERaddserver(gobject.GObject): __gsignals__ = { "connect-success": (gobject.SIGNAL_RUN_FIRST, None, ()), "connect-failure": (gobject.SIGNAL_RUN_FIRST, None, (str,)), "sync-progress": (gobject.SIGNAL_RUN_FIRST, None, (str,)), "sync-success": (gobject.SIGNAL_RUN_FIRST, None, ()), "sync-failure": (gobject.SIGNAL_RUN_FIRST, None, (str,)) } connectThread = None def __init__(self): self.__gobject_init__() def connect_server_async(self): # begin connecting self.connectThread = Thread(target=self.connect_server) self.connectThread.start() def connect_server(self): protocol = ["http", "https"][self.ssl] self.url = "%s://%s:%d" % (protocol, self.host, self.port) print self.url self.connection = xmlrpclib.Server(self.url) self.connection_events = xmlrpclib.Server(self.url) try: self.session = self.connection.session.login_with_password(self.user, self.password) if self.session['Status'] == "Success": self.is_connected = True self.session_uuid = self.session['Value'] self.session_events = self.connection_events.session.login_with_password(self.user, self.password) self.session_events_uuid = self.session_events['Value'] self.connection_events.event.register(self.session_events_uuid, ["*"]) # tell the controller that we've finished self.emit("connect-success") else: self.emit("connect-failure", self.session['ErrorDescription'][2]) except: self.emit("connect-failure", sys.exc_info()[1]) def thread_event_next(self): Thread(target=self.event_next, args=()).start() return True def fill_alerts(self, list): #FIXME priority: 1 info 5 alert self.all_messages = self.connection.message.get_all_records( self.session_uuid)['Value'] relacion = {} for ref in self.all_messages.keys(): relacion[self.get_seconds(str(self.all_messages[ref]['timestamp']))] = ref rkeys = relacion.keys() rkeys.sort() for ref in rkeys: message = self.all_messages[relacion[ref]] self.add_alert(message, relacion[ref], list) def sync(self): try: # Get all vm records self.emit("sync-progress", "Retrieving VMs") result = self.connection.VM.get_all_records(self.session_uuid) if "Value" not in result: if "HOST_IS_SLAVE" in result["ErrorDescription"]: # TODO: csun: automatically connect instead error = "The host server \"%s\" is a slave in a pool; please connect to the master server at \"%s\"." % (self.host, result["ErrorDescription"][1]) else: error = "Unknown error:\n%s" % str(result["ErrorDescription"]) self.emit("sync-failure", self, error) return self.all_vms = result.get('Value') self.emit("sync-progress", "Retrieving hosts") self.all_hosts = self.connection.host.get_all_records(self.session_uuid).get('Value') # DEBUG for ref in self.all_hosts: print "Server version is %s" % (["%s" % (self.all_hosts[ref]['software_version'].get(x)) for x in ('product_brand', 'product_version', 'xapi')] + [self.all_hosts[ref]['license_params'].get('sku_marketing_name')]) self.emit("sync-progress", "Retrieving pools") self.all_pools = self.connection.pool.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving SRs") self.all_storage = self.connection.SR.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving tasks") self.all_tasks = self.connection.task.get_all_records(self.session_uuid).get('Value') for task in self.all_tasks.keys(): self.tasks[task] = self.all_tasks[task] self.emit("sync-progress", "Retrieving VBDs") self.all_vbd = self.connection.VBD.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VBD metrics") self.all_vbd_metrics = self.connection.VBD_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VDIs") self.all_vdi = self.connection.VDI.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving networks") self.all_network = self.connection.network.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving PIFs") self.all_pif = self.connection.PIF.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving PIF metrics") self.all_pif_metrics= self.connection.PIF_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving PBDs") self.all_pbd = self.connection.PBD.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VIFs") self.all_vif = self.connection.VIF.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VIF metrics") # FIXME: csun: all_vif_metrics == all_vlan? self.all_vif_metrics = self.connection.VIF_metrics.get_all_records(self.session_uuid).get('Value') self.all_vlan = self.connection.VIF_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving NIC bonds") self.all_bond = self.connection.Bond.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VM guest metrics") self.all_vm_guest_metrics = self.connection.VM_guest_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving VM metrics") self.all_vm_metrics = self.connection.VM_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving host metrics") self.all_host_metrics = self.connection.host_metrics.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving host CPUs") self.all_host_cpu = self.connection.host_cpu.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving pool patches") self.all_pool_patch = self.connection.pool_patch.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving host patches") self.all_host_patch = self.connection.host_patch.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving consoles") self.all_console = self.connection.console.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving subjects") self.all_subject = self.connection.subject.get_all_records(self.session_uuid).get('Value') self.emit("sync-progress", "Retrieving roles") self.all_role = self.connection.role.get_all_records(self.session_uuid).get('Value') except: self.emit("sync-failure", "An unknown error occurred. See log output in terminal for details.") print "Synchronisation error:\n" import traceback traceback.print_exc() else: print "sync-success" self.emit("sync-success") openxenmanager/ChangeLog0000644000175000017500000001445611636446664014067 0ustar rrsrrs04/06/2010: - Implemented "New Storage - AoE" (experimental mode) 06/05/2010: - Plugins now works with custom fields - Added two new demo plugins: NagiosWebUI.xcplugin.xml GlpiWebUI.xcplugin.xml 04/05/2010: - Implemented custom fields - Removed check if a network has a pifs - Fixed a problem on search tab, if cpu load (for error) is greater than 100 then set to 100 - Recoding 01/05/2010: - Updated Performance tab: rrdtool is not more needed, now graphs are auto update and you can put your cursor over point and see the value and data (performance tab now is in testing mode - Updated usagebar images 25/04/2010: - Implemented "maps" tab for testing, you need install graphviz 23/04/2010: - On Console tab added two buttons: send ctrlaltdel and enter fullscreen - Fixed "send ctrl-alt-del" 22/04/2010: - Console works now with older version of gtk-vnc - Keymap should be (partially) fixed, please, is important if you found some problem with keymap tell us 20/04/2010: - Fix a problem exiting from OXC when a vnc connection is still opened - Performance tab should be finished now 18/04/2010: - Toolbar buttons: Force shutdown and Hard shutdown - Now network tab on host shows the same information than xencenter - Fixed a problem with glade with oxc.glade 11/04/2010 (2): - Implemented "updates manager", tomorrow i will fix "upload update" - Now if NIC is disconnected then speed is not showed 11/04/2010: - New VM window recreated and improved 10/04/2010: - Implemented: "Get Server Status Report - Fixed bug 2984515: "Doesn't show options after VM creation" 04/04/2010: - Implemented in experimental mode: VM Performance, i need testers 01/04/2010: - NEW FEATURE: added migration tool of xend based xen virtual machines to XS/XCP, thanks to dmarkey - Now when you add a new server to pool shows a dialog saying "please reconnect to resync" 31/03/2010: - Now you can set if a disk/cd is bootable or not for each VM - NEW FEATURE: Download host logs (all /var on unique file tar.gz) 22/03/2010: - I think now is fixed insert ISO/DVD to VM 21/03/2010: - Implemented: Repair Storage - Implemented: Delete server from pool - Almost implemented: Add server to pool - Fixed: broken storage wasn't detected so fine 20/03/2010 (3): - New feature: "Take Screenshot" on Tools menu 20/03/2010 (2): - When you disconnect from server, update alerts 20/03/2010: - More visual colors fixes - For ubuntu lucid: gtkbuilder on pygtk has a bug, get_name() doesn't work. I change code for alternate way to work on ubuntu lucid and other distros 19/03/2010: - No gtk warnings now - Fixing some visual things - Experimental plugin implementation (including a OpenFiler example), you need python-webkit 17/03/2010 (2) - SO information is showed now on General Tab (copy from xencenter 5.6) - If memory host information is zero, doesn't throw error now 17/03/2010 - Tab search should be not frozen now - Start VM after vm creation should be fixed now 16/03/2010 (3) - Improved: now search tab should be fastest (threading) 16/03/2010 (2) - Add network to VM now shows all networks - Possible solution to missed vms under servers (on pool) 16/03/2010 - Fixes: Width of management interface's confirmation dialog - Color on license warning window - Now when you disconnect from a server inside a pool, disconnect from entire pool - If default storage ref doesn't exists, doesn't crash on new vm wizard - New feature: Expand All / Collapse children 15/03/2010 - Fixes: On create VM ISO combobox doesn't show correctly the info - Now hosts are added sorted under pool - Fixed hidden templates (xencenter has a similar problem) 14/03/2010 Fixes: - before hide Local Storage also hid shared storage - now logs tab shows the logs sorted by timestamp - problem causing a crash: start a vm on logs tab - problem mounting DVD on a VM - If a exception occurs on a main thread, exception is showed but application continues with the thread - Right click on a snapshot (snapshot tabs) crash the program - Enable actions for a template in a top menuitem - Now you can delete a template from top menuitem - Add disk to VM now works properly Features: - Now progress window works on "connecting" process and not only on "sync.." - Some tabs hadn't a horizontal scrollbar - Migrate Server between servers - Delete VMs from tree when is deleted 11/03/2010 - Two days of fixes 07/03/2010 - New feature (not in xencenter): Pool -> database dump and restore database 06/03/2010 - New features: On help menu -> "Check for Updates" and "XenServer on the Web" 03/03/2010 - Implemented (i dont know if fully): Server -> Enter maintenance mode 03/03/2010 - Implemented: Start in recovery mode (On VM menu) 02/03/2010 - Fixed: On "New VM" oxc style was always used - PLEASE TEST IT: connecting now is on a thread and window progress now is showing a progress 02/03/2010 - Now all menu options do somethin, expect reboot/shutdown server and send ctrl+alt+supr (not implemeted yet) - New FEATURE: On server menu you have "Show dmesg", it will show a dmesg of server 01/03/2010 - Fixed problem when you edit a VM, the dialog doesn't close, thanks rsampaio - Now you can select text on labels with information (general tab and log) 28/02/2010 - On Pool General Tab now shows fully/partially patches applied 28/02/2010 - On Server General Tab now shows patches applied 28/02/2010 - Now when you connect to server you can select SSL connection - When disconnect from server, you will go redirect to home window 27/02/2010 - New Storage -> Hardware HBA - When a task is completed, show "completed" and not "100%" 21/02/2010 - Pool tabs - When a VM is booting or is pending of shutdown the image of vm now is orange 21/02/2010 - Install license key 21/02/2010 Server: - Backup server - Restore from backup Storage: - dettach/reattach/forget/delete 18/02/2010 Install Xenserver tools 17/02/2010 Pool Properties Delete Pool Disconnect Pool Fixed frozen problem and periodic crashes Fixed memory set problems Fixed new vm wizard problems Change Master Password 08/02/2010 New Storage 01/02/2010 Add pool/delete pool almost finished window.py now has code commented 30/01/2010 Management interface Now dialogs and windows could be accepted pressing key enter Double click on disconnected server now connects to server Detect if pool is created or deleted and refresh the left tree Partial code commented 26/01/2010: Import VM is 100% implemented openxenmanager/window_alerts.py0000644000175000017500000000760311636446664015544 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python class oxcWindowAlerts: """ Class used to manage window alerts """ def on_btclosewindowalerts_clicked(self, widget, data=None): """ Function called when "close" button on window alerts is pressed """ self.builder.get_object("windowalerts").hide() def on_btalertdismissall_clicked(self, widget, data=None): """ Function called when "Dismiss All" button on window alerts is pressed """ # Show a dialog asking confirmation self.builder.get_object("dialogdismissall").show() def on_btdismissallyes_clicked(self, widget, data=None): """ Function called when accepts "Dismiss All" dialog confirmation """ # For each alert, dismiss it self.listalerts.foreach(self.dismiss_all, "") # Clear list self.builder.get_object("listalerts").clear() # Update number of alerts self.update_n_alerts() # And hide dialog asking confirmation self.builder.get_object("dialogdismissall").hide() def on_btdismissallno_clicked(self, widget, data=None): """ Function called when cancels "Dismiss All" dialog confirmation """ self.builder.get_object("dialogdismissall").hide() def on_btalertdismiss_clicked(self, widget, data=None): """ Function called when presses "Dismiss" button on windows alerts """ # Get selected alert selection = self.treealerts.get_selection() if selection.get_selected()[1] != None: iter = selection.get_selected()[1] ref = self.listalerts.get_value(iter, 4) host = self.listalerts.get_value(iter, 5) # Remove from server, if returns "0", alert was removed if self.xc_servers[host].dismiss_alert(ref) == 0: # Delete from list self.listalerts.remove(iter) # And update alerts self.update_n_alerts() def dismiss_all(self, model, path, iter, user_data): # Remove alert from list listalerts = self.builder.get_object("listalerts") self.xc_servers[self.selected_host].dismiss_alert(listalerts.get_value(iter, 4)) #self.listalerts.remove(iter) def update_n_alerts(self): """ Function called to update number of alerts """ self.nelements = 0 self.listalerts.foreach(self.count_list, self.nelements) self.builder.get_object("lblnalerts").set_text("System Alerts: " + str(self.nelements/2)) if self.nelements: self.builder.get_object("imagealerts").set_from_file("images/alert.png") self.builder.get_object("lbltbalerts").set_markup(" System Alerts: " + str(self.nelements/2) + "") else: self.builder.get_object("imagealerts").set_from_file("images/ok.png") self.builder.get_object("lbltbalerts").set_markup(" No System Alerts: ") openxenmanager/PixbufTextCellRenderer.py0000644000175000017500000000613711636446664017255 0ustar rrsrrs# http://www.daa.com.au/pipermail/pygtk/2004-September/008685.html #!/usr/bin/env python import pygtk pygtk.require('2.0') import gtk import gobject PAD = 3 class PixbufTextCellRenderer(gtk.GenericCellRenderer): __gproperties__ = { "pixbuf": (gobject.TYPE_PYOBJECT, "Pixbuf", "Pixbuf image", gobject.PARAM_READWRITE), "text": (gobject.TYPE_STRING, "Text", "Text string", None, gobject.PARAM_READWRITE), 'background': (gtk.gdk.Color, 'Background', 'The background color', gobject.PARAM_READWRITE) } def __init__(self): self.__gobject_init__() self.prend = gtk.CellRendererPixbuf() self.trend = gtk.CellRendererText() self.percent = 0 def do_set_property(self, pspec, value): setattr(self, pspec.name, value) def do_get_property(self, pspec): return getattr(self, pspec.name) def update_properties(self): self.trend.set_property('text', self.get_property('text')) self.prend.set_property('pixbuf', self.get_property('pixbuf')) self.prend.set_property('cell-background-gdk', self.get_property('background')) return def on_render(self, window, widget, background_area, cell_area, expose_area, flags): self.update_properties() ypad = self.get_property('ypad') px, py, pw, ph = self.prend.get_size(widget, cell_area) px += cell_area.x prect = (px, cell_area.y, pw, ph) tx, ty, tw, th = self.trend.get_size(widget, cell_area) tx = cell_area.x + (cell_area.width - tw) / 2 ty = cell_area.y + ph + PAD trect = (tx, ty, tw, th) self.prend.render(window, widget, background_area, prect, expose_area, flags) self.trend.render(window, widget, background_area, trect, expose_area, flags) return def on_get_size(self, widget, cell_area): self.update_properties() xpad = self.get_property("xpad") ypad = self.get_property("ypad") xoff, yoff, width, height = self.trend.get_size(widget, cell_area) pxoff, pyoff, pwidth, pheight = self.prend.get_size(widget, cell_area) height += pheight + PAD + ypad width = max(width, pwidth) + xpad * 2 return xoff, yoff, width, height gobject.type_register(PixbufTextCellRenderer) if __name__ == "__main__": w = gtk.Window() ls = gtk.ListStore(str, object) tv = gtk.TreeView(ls) pbtcell = PixbufTextCellRenderer() pbtcell.set_property('xpad', 5) pbtcell.set_property('ypad', 3) tvc = gtk.TreeViewColumn('Icons', pbtcell, text=0, pixbuf=1, background=2) tv.append_column(tvc) w.add(tv) ls.append(['Error', gtk.gdk.pixbuf_new_from_file('usagebar_1.png')]) ls.append(['Warning', gtk.gdk.pixbuf_new_from_file('usagebar_2.png')]) ls.append(['Info', gtk.gdk.pixbuf_new_from_file('usagebar_3.png')]) ls.append(['Question', gtk.gdk.pixbuf_new_from_file('usagebar_4.png')]) w.show_all() gtk.main() openxenmanager/scripts/0000755000175000017500000000000011636446664013772 5ustar rrsrrsopenxenmanager/scripts/dmidecode0000644000175000017500000000114011636446664015626 0ustar rrsrrs#!/usr/bin/env python # show hardware info with dmidecode import XenAPIPlugin, time import subprocess def doexec(args, inputtext=None): """Execute a subprocess, then return its return code, stdout and stderr""" proc = subprocess.Popen(args,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,close_fds=True) (stdout,stderr) = proc.communicate(inputtext) rc = proc.returncode return (rc,stdout,stderr) def main(session, args): (rc,stdout,stderr) = doexec(["dmidecode", "-q"]) return stdout if __name__ == "__main__": XenAPIPlugin.dispatch({"main": main}) openxenmanager/scripts/README0000644000175000017500000000034011636446664014647 0ustar rrsrrsHere a list of scripts that can be used with openxencenter, you just have to add in your xenserver/xcp server in the /etc/xapi.d/plugins/ directory and remember "chmod +x plugin" dmidecode: show your hardware info on a tab openxenmanager/TODO0000644000175000017500000000115011636446664012770 0ustar rrsrrsThread for connection -> cancel button on progres -> update progress New RoadMap: Server: - Change server password (done) - Enter maintenance mode (done) - Backup server (done) - Restore from backup (done) - Install license key (done) VM: - Install Xenserver tools (done) - Start in Recovery mode (done) Storage: - dettach/reattach/forget/delete (done) Other: - Install last patchs - New Storage -> Hardware HBA (done) - Send Ctrl alt del - Clean code Use text: HOST_NOT_LIVE: Unreachable VM_REQUIRES_SR: Cannot see required storage HOST_NOT_ENOUGH_FREE_MEMORY: Host has not enough free memory openxenmanager/images/0000755000175000017500000000000011636446664013550 5ustar rrsrrsopenxenmanager/images/newvm_32.png0000644000175000017500000000354111636446664015721 0ustar rrsrrs‰PNG  IHDR szzôsBIT|dˆtEXtSoftwarewww.inkscape.org›î<óIDATX…­—ylTÇÇ?óŽ=¼~^Ö‹ ®q¸( bhˆhI"+mš UB©•BÒV ©DUD"E*RÕ‹4UÔÒ „rTHHH!U•Ö¤.¸ðÉQÛ¸kÇë]Û{¿kú‡³Ë¾õ:@Õ‘~Ú7oÞÌ÷3ßùÍÛ7H)ù¿ÅOY _gÇôRJÊË3ßÛ¹WlEÈ(wXÚjû>¾¼ô·£[ÎVkˆ ªeÜ¿ÿñÒ½r€g¶½ôƺµ«¿ßþðFZšã8Žg€"uñºøëº.®ëâ8@.ÖýÐÁÒ½b¤Óº{ú9ßÝg'§fÚßÞ÷‹“Zqðo¾ðr䞦ÆçžýÖ“(Š‚ßï¯*^.\YWÔÝ&hhš†ã88Žƒëº†A8\G4²@{÷ðÑ]BˆÍRʼRð «}ÓÆõÇqþ'q)%œ$`4’H$Jâ%»€®ë´4/fÉgš6M%é ­¡>B(º¥8]¿F\|sN»˜øšB¤'†<âÊø?¨;³Û¶Ñu£®Vš…B)s!Ä-gî>ðöà!”O#3—Ú »hK"Èø™YqÛ¦føUÂ/ÒWÿ ²Ù,®ë"@µÊ«R|Nù {±2¨/¢ŒžMØ€„Ú…(f2u•ðÄw¨m=ǥ䈥ƒ¥¼(Ÿ¤`>ájnÝO2½µe zs/zïw† "BT=E$ð#«V2t¶ÀÙø–/_^JH)ó|šõ•õðÚ—?s¥f5þu¢,X®KÓã[Ð6’ºzš¿\ØD[[@ €ù¸™—ê™19J]Í Ì‡Á‚­ƒU»Ìq]16µÆYb}ˆ?;„‚¢(P¶ÔÚ|¥úÇ]ˆÑ¿¢Xÿ8„uê(!âÞzàŸ§0nÜàî/µqà=dá-ì”]<½&/¾ý~Üäòð­jšÉù—af Øf7CÕgЂü>›@H¢×ø€8>pu@€;…95C6éÊf2:™|”¬YÏÙË–<øÎµ÷ûãòE=0Á(úŠÍÒ’M¥˜žž&•J‘J¤H§Ó„§?â‘5Fó`6 gFÏ·òçìvj ƒP(„Ñh ë:ÓÃgèÿñ<04 šBB¡Á`h4z³Ý6 õÿ½.O6&Pl‰ÏÙ Ø¡eÙOº£8+·—úd³Y‚5Ý|‚éMÂÙ-2wŠÙë8ާ=øïŸà:I\‰r²û)2caœ\­cpp…¤@O¾‰ž¾‚®ë躎ªªžÉÎ 0Ÿxñuòù.ãCmüþúv´•Ï¢îÂÉBZ."üÕ£\šÙ†]péhªŠ¦i(Š‚({ylÛ&“J¥0M³$^RJ¤•#4ô3F“[9>õ<=öuÖ®]‹ºkvø‰DX½åU†›Þ"—Ê¢\Ø…®ëhšwÕ=5×q»i¢àóùðù|躎EQLg(òCzc O<ÑF0DUU´º¥Xq0ëB „྿L|Ycî¢ÙL˜@º:×uÉçó8ŽƒeYhš†®ëäÚ¡P`ÃÝa4M›×4´àª!DýgK .ÄÙ¼—\.‡6™Lfºê8Ž èBµ0 UUK!„@­à SŽ–Š¡i†aÐÙuQ^îééevÓJ¯nu€¢õ塪ª§.„€† H·Ã0q¹wDZ±íÙÈ øtMœý¨ãp]Jé¨ü<³¨ãÙV®²„®‹½<òÅlþJ;¶mcY‰D‚x<ÎÛ‡Ž\Ž\bs– ÒJË!Š×åÏ9ŽC¿õ +–ÝËâE ¥/¢ééi’É$ÿàLfÿ¾_í¥”ùÛ˜¤<ÉŠe``€Ž‘Ü¿j%¶m{Ä/öô;¯íÙ½7›NŸúŠ}n{ æs¦¼o<'—ËaÙ6ª¢033C"‘`äFŒŸïÞóÎØ†O礔VU€jIx'¥øâ2MÛ²˜œœäʵ!¹çµ×ötŸûpZJ™-ïs@g&•®:påé©xOJYrAUU‚Á B²™,étŠS]Ö+»wì»t 8%¥LVŽSÊWÑþÖÓw5_M¤²^y:* ¦( T¦¦¦8òþŸ¦_Ú¹ã•Á¾KïRÊqªÏÑlë¶û–/myîûZinjœ=íTìõÊ­Xž¨ÇŽŸ¦íZÿìüבÿ;dær@—”2WÕÚJ€Ç¿öÔ–`¨æy¤ht]G/îé‚2a×qÌÃ×.wŸ;ÝaYÖuà0,¥´ç¯ ðÉ_e-Єªuú”"q`RJ9wKU)ÿ„ùõšÏ’IEND®B`‚openxenmanager/images/user_template_16.png0000644000175000017500000000100211636446664017426 0ustar rrsrrs‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 9/&‹@‚IDAT8Ë•“¿KÃPǿצ¿ upÛpTqðOH Y2t°c·;Á¿Á­ƒßà *N‚Z(HIMQ,•‚`îÅÁDÓ´)ú…ãàÝ{÷>w÷U«Õ{MÓŽ…ÐuaoÆ{§Ó4›Í~½^ï;G—爈ºÝ®gš&bôàÀ€›­Zï!ºABÀ4M8Ž)åwV¢ ÎD$¸¾_¢ë:@J‰ápøsØ÷ì›$"²ˆ%€R©´Œ€I¬x"š#ð%Š?‹Åh ø`ÏËÈD"Á+ àìÊŽÆÝ0Ų&&„suî2ZF•ƒÊ…œ,r\.äØO´š¬¾…‹ˆàN{'³ëŒµýÕìñ“± Û²ñâ|ºÏ¯SÆ2®„¹)@jï$çÔo–—žB«²" OBo€ˆ¸sýOÏç—]À[P2Nk®ën·Ûm03Â^Q”Y£Ñ`UUÓªªn$Ë5cá7âÚ<}ô¢k_z°N(õŸIEND®B`‚openxenmanager/images/ok.png0000644000175000017500000000177211636446664014676 0ustar rrsrrs‰PNG  IHDRàw=øÁIDATH‰í•kL›UÆŸó¶öò¶o-¥-—–C:ÈXt:‹ËâÆâ—l^ ,[²¨_\²,11Ù–LM4F'‚â%›9„hÔ¡²Éè@» £/µ7 Ö•"o{üâÜœÛøæ“œOçœßóÿŸòB)ÅRŠYRúÿ ¥¬!Åûô?f¼£1Üuö3R”ÌëmØ~Ì”ÍäZ䯒ôë›”Ò;ZòOQœÿfn¬ë¢î9º‹¶£òræœd+Ò)¥wÖ¢–fûrN°µš1Ÿo€¿ÏÞ‚·*ÞΗÎˬÒMD}ÛŠjR”5±ü̇/ÖˆÌÜþ?OB<]€ÕÙŽQ‹bó7Ì å¨L­: ©Õ‘.g?&EË&³ìË«³û[\ˆÀ.ô@–-F ÑâRǨƒŠç6 6¾nÀÖNð ùÄËÝ;µWÓÌŠCdU"xR)ΊdÛ«¶×0­MŽáüüY$ÔP4³NŽ„¢c&ß7!/ðwºJ)—Nij|¾¥Døc¶ò“†{ÃIæ¤z¦`!<­-Ìäö~´í¦=зàĈðô©:È]ÜmAžˆâëf,Ôwí#ßGT¹Bù½g*×Óàœs¢{¬ öËTÅð2²Z’ SÆ3ÏT•×0ßOZá£nðô2t©:`X‚³—g”qÓ¬•zÅÐ?¡U® ÙÓ€ÂKE˜f¢è˜° {[ŸC’öÇ;W­Ï®¶,«bº®vbœxá"£Ð¥ê —Žó¼$/nšm¤ÞŸ”™}—ò„ÊÂÓÁãÞX®4#.Š!&š‡%ÔŒS»úÒ•£ZÛÞg÷‹º£'àù๡Ók1sYÀ`ëƒ]GMÑ÷o†¹×™{ô›d¶N½A¦1ê3ð·œX…#`ê Ø"Vøé8œ1X¹~G=Ÿ÷ñêÒ{J\›#žDð€òuRª”ªêÚ²2™ÕÈq¿äA¤I ˆ“‡›þ…œ…ÇåGOmŸµYgê_ëHXyBà*ÉFED]_RþX²,U ÕB)æà'ã`¥,xÞž/úù‡ËrMíô. „ÝÔnÚ_>óÒɯìñ” Œ’`Z…QjĘ3ˆþÃçO½¶æ–à À_1ÛÁ0¶ºŸB²+V’ø\AüúåE~ÇÞž¬×·ÝÀâiºº{EéŠÊœÐ+gwм÷9¾¦‡ ÿ5mÿõÀ† >m|#íô~º;óvâü¦!ßm-ùŸü".óm¶½IEND®B`‚openxenmanager/images/usagebar_8.png0000644000175000017500000000101311636446664016271 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<ÀPLTE…qÓÓÓpppêêêÅÅÅ¿¿¿¯íB†m߯ê…A–A‹ ¯üv¯æ‡mè¯øyB‚n×!C~¯ò}mñ nà¯üuA‘®üvÝë ÞÍìûuÞÁ ~~~Ýݾ¾¾ËËËìúvqqqëëësssvvvÙÙÙçççãããßßßéééÜÜÜzzzÄÄÄÞÃ!‚‚‚ÝÛ…ˆ ìôyíäí܆ìë}mèménØ!n× ®üunßmò B~ÌÌÌÿÿÿa¬¬¤@tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ{±D•IDATxÚ¬ÑG‚0@Ñob+ö ˆ½"(Òbâþweâù È€7¹ xð-%hÖ¼(¦æDŒù>cJ/”n˜?ª¾1V´Ð=WÝNšö’Ä6'rÝ0 ß7)×Ržhð çͺ16,u¯}Uu?#0ȳ<¶Ì p'ÞÕTÞ!FBˆ®9ð/&dC9 PÎðŸ1•Zö¼*GIEND®B`‚openxenmanager/images/import.png0000644000175000017500000000230111636446664015564 0ustar rrsrrs‰PNG  IHDR szzôˆIDATX…µ—Mh\UÇçÞ7of’ÉLbLiA ØJ\D mhAZ×¢HÅ.\HT\Ú…B%‚­QlW%è¦iWV)Õ¢´i‰mý‚B‹Õ€µÉd2óÞ».f&MÚ™LÍ´8ðî{÷ãþçÜs±1ÆÇÇ{‹ÅâÛ@Š5@D°Öê}ûö™šš:<==ÍÌÌLËq@†xïǵÖ/­eñ:zzz8wîÜçÜöÍ›7>uê—.]ZuŒðÞ#"¡µ–v,IÖ¯_O.—›tÎmfãÆ­ ˆÎ9D¤-sÎÑÝÝM__¹\nÒ³mëÖ­ 4%,o8çnQìÆpΑN§Éçód³YJ¥ÒdEÛGGGwttpæÌ™Æ 8çêahËŒ1 !"d³Yz{{) “ƒƒƒÏîÚµ«¹"‚÷ï}[ ÄqÌ–-[YzW#6vìØ±CM Ôh—@DÇ+Þ…a˜d2™†ýo«Í`­mš_+°ÖÞZë¦Î-Õ;å}«ù—BP/&w-hwÿ·Âjá –w¸=D„Àg±Dx©.ZϱFXÊç\Û†MaL™‚7¸*?á­,}óM| n$°V¯S¾‹¿õ÷œMíefî8÷f¶×xNð’®Ðuä»BÀ#çÏó¦}âSx gÓòkås’â^ºY´ÿ° þÀa‰M±+<úàÁŸŸK çc….ŸÿrÏTÜÀw2§cJíá¯Ù T%YðŽÓú•ªz‹<¦Lú)©š^ Úqäv®‰€xBó{æ N÷E h•ÇZ¡žëó¥a•Ú’ÊêŒ1öúq|«»@û åà 'e/fŽEI'ææž«;‘±õ+sÀ˜›§Y9eÈ9ʉù¨,–TFZrn+¼UUõ*ت[ y³ Wê$v‹à4"kc Z°NV*к[:YÇó… ¾+àäâgd¥€j!w#8%X§®ç€1¦ej=1Ä<šy‚aͽŢ›'TÏûfPV‰sR­„ËCp+æ¼£b‹tJ;z2>ŵ¸HlÆPJbŠÉbÍ*DÆ“˜`ɬU3 +wA’$ÿË Hˆ©ðDöe6sdî=*>Âãx&ÿ&÷…ãq”]ñ›o£OG®u\Ð5Ñ%•Õ¦tÍ´%óxÊvž>ÝÏΞý|Uü˜éÊ×XopÞ`1t¦òôŸåW^¼)É ~‡SkÍh¨î+†Ñ«ܟ¢Kß«•%ë : ··—b±¸âÎ(»w樂¿Ÿ 6¼žÉdÞ¿7#-Î;<ÕŸr¹üÁìììkét¥J)*• /^D&&&BkíCÖÚ¾t:ý¤ˆÜôP=ª @õø:tí¹< õ1Þû(Š¢J)‘«Àp¸¬µ>Xk ÀÓZëG’$Yç½/9ªɹe“g¨Ö×Vqò€©ñ"âµÖ›€(ÕÌÓÖÚËAÍ»€‘Œˆ„5/C cÙ‚]-¼¿Q…ùeí…©¨fq­ÍZV ii5)IEND®B`‚openxenmanager/images/tree_halted_16.png0000644000175000017500000000132511636446664017045 0ustar rrsrrs‰PNG  IHDRóÿaœIDAT8¥RMKQ=ïM¾gR™ŒD‰ºp¥mqg‘nýX*DWÙ‰¸j»܉®´]g!EDôøŠ"H Y¨%M-ÕIL3If2™yïuXÛU/\¸¸çÞsï!BüO¸þfææÈÏÝÝà¼@i¦=‘øÔŸJ=™FþÜàc08©*Ê»Ððp¯ÒÝ PŠj.‡ÂÑÑ×¢®¿yU.ïý“à@Q^wôõ­‡“IBdù±0 ä77ÅÕÉÉÛ—•Êû{œÞûÁàh[,¶¦ÎÎx½`…¸`œ£žÏCx_Ô¥(KKpG"ëúX]…¨T­Õ~·ûùàííõ#¥[ZLÆöü„ÄZxîp €Àâ›_’&J¥ô#¥ÓiìÎÏŸü’åAXùÎùùçâ‚sqÅù¹ ¬dyðC2™988x°òââ"ÙÞÞö–J%¿mÛι_áBxU €€E)­QJ —Ëe*ŠbŒÕˆ¹\ëëëôøø˜jšF ÃÇ¡Œ1”Ráv»¹Ïçã­­­,ó……ÇñqÃFû„¡ÑVIEND®B`‚openxenmanager/images/usagebar_2.png0000644000175000017500000000066411636446664016276 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEB~ãããççç‚‚‚ÜÜÜmò ÄÄÄÙÙÙzzzßßßvvvééé¾¾¾qqqËËËsss~~~ëëënß®üumèmén× nØ!¯øymßA‹ ¯ímèB†®üv¯ò}nà¯üu¯üvB‚mñ ¯ê…A‘A–C~¯æ‡n×!ÅÅÅ¿¿¿êêêpppÓÓÓÌÌÌÿÿÿý¯p2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïvIDATxÚ¬Ñ ‚0„áqAD\Û²(((îØG¹ÿå,É»ÀKø“Éw€A?JxéÛEë+3³6¶ö Ï{àË™}74ƒS’”ÆTÌÒ¹­s18«Z©cÁ¬Zß"ƒ&ÍÒìû`v?ßz.ÀÛïÃmˆ(ƒqÿ 0àGK¹þ>¯IEND®B`‚openxenmanager/images/repair.png0000644000175000017500000000203711636446664015542 0ustar rrsrrs‰PNG  IHDRàw=øæIDATxÚ¥”]lUÇÿwfgwvÛ-íb?v·´Aj,õmHªZ±PädµÔjH4Dy’Ÿ}”D_ÔV… aM|ÀT!›Ö’Ôh” "`¥Ñ­Ûýh·ÛÙ™ÙOO—”TÛî2™›Lîœûÿó¿÷\†x!ú;Z>äÚ¶f~ kТ㷪b·®Ä°rÅ î¾Æ·`Ž~;†™Õ¡ÅÔfŸ¶4õÀõ@·Í?Ùcø[0Ñ +£¡“ƒ¾ÂBbu\Y€YˆïTüÞúgŸ€1sÑ@îÎâ–]НŽK•ƒeæš¶Žx:÷GÀA¨¢)eª&9Ó|ÏÂg:|]¿¼ B-ÑMûۺ͟~8°lp¤´4­Nצ3Ù§vÅ«=”?ú¼<‹&á~¿ñøÁ“Öød2L»8˜mCõ…àÝ×ýÓ3rf逿˜+\ݵ;â©­„yõ m°Ð @/ Ì)ÂRUðϪ×Kü W¸îÕÞˆ‹üÈŸïÁ=$ÊŠÛ´ŠUpü¤aN1:7 ¸ 1è?!C;7 Áío‚Ï ÎÃßµ‚ªÐT³9’¦÷íÌçÎoð;œáÆþÞÈrpæËaˆË™ÓÑñTÐÜâ)å(%¾(õ4¾i/äN­¬]pB¸ùÈÁˆ ñs— Š"f£ŠÄmÊ<–TOè¹äÿ­_ðe¾õHoÄ0 LD.¢Âu7ó‡*D“3Jp—–K¬¥Áî5M,ÖA0¨#T…v[b7êU—µ=Ì.YÌ,H"ºÎƸ`m×=ºrðl™Yrh:BaÛ4mIÅ4é Çð…ÊRž°á§X1I&V1_È“ì”Ä 7óx5…»En— ¹©["Ú®dzÙ•¿äj´½>ÒFÛ Ðv]pÛxT¥rPU( X¶MGÔÀð{Éß-‘¯ ñ(ma2õë4¯nN¡ÎTš²!ºÂ°ìj”‡|e¡B"-“nÝ 3BRüip@PoÔÈææQuƒx2Hf2ƒkšBTáÂÅÉE:CO§t¬¶í3PTp³iQ¼ª¦MµaSqZºŸ5}qF'g™ŸË x ŃýBÈ{;¸9|‘uýϳTuH„½lMGЇªEE÷"/¸à´ÁgÄh›&Æ'oýHÁ˜ êa»w)ÑIùÖ(ÄvÔÒBž®xœìÈ0š®ãlé§P±éúØÖAÓÔS-xM EUåm˲ŒÏp¥t«‘¯¹â;ƒE€ÓN‚7ì×Ù÷Ü>©¤¹\N=v ÅÆÛ×Ïâ<š®Ó·¥M÷±:æg`Md}½´”µô#£Ó,«hgC'8ëù9+ÉnŸâDá>lÁP Nm<É ¡A¡ôööŠ÷¯¼¸ƒFæ<‘Ö¢±Ù‘aÌr…ÉÜ2_ü\ÈN”¾½8ŽÕlÒÓ¤Øy“Œç"D[apÀgû9A„ ‡g?4×u–ß9ƒˆظƒéL#ÚÁî—wbÖª„#õo8rãcfßš~¨ >ýL©}ĵ? õjUÅÀÀÀ x Š7¶Ý£1jiÛ"½ª‡ØoQö= Ûf›wß|gïå±ËLJ®‘MÞ—ò+’üO sCCÔ£M,ÇâÙmÛH¤Ó"åÌɧj§¹´p‰òT™µýÇÇÚc|ÙýHØÛ³÷ÑëÚu]îÌÍÉÞtZˆA*Ÿ—¯¤4Ydwñ%f{sd<7Ù°n=羿÷PJùXgÑ^<¹çÚ¹õÜ3rç…]òý[È•÷âÿOÿßô;%Ê$ñtTg­IEND®B`‚openxenmanager/images/server.jpg0000644000175000017500000001752211636446664015567 0ustar rrsrrs‰PNG  IHDR€€Ã>aËsBIT|dˆtEXtSoftwarewww.inkscape.org›î<äIDATxœíydG}ç?ù^ÝÕU}ÏôôL÷š[#¡]hÆx™ÐØb½%mÀ:@6° ãŒ!66Ø `Øõ8VÈë°5 #‰i4’æ>4wß]]÷{ùÛ?^ÝU]wuf¾õ^¾#™ùÍ_þò|JD¸ˆ ÆB p ‹‹¸Àq‘8.àÇE\à¸H€  pã".p\$ÀŽ‹¸Àq‘8\ -@;ñŸö†Ó:Ò¸B.— iQ!CIIJ$b`E܆?ò?ÖŽM/´¬í‚z+Ýw¤{¥XúF´b½ÖëP•¯ˆûE±ßö#ìS.ãùWOkÔ ƒ· î=Ð}Jß!p;pE‹‚٭ৈñèCë&v·(Œ¶â¼%ÀöS®3C[ ävnVÖò¼Nƒvânº†»fŽ)ÅO5ê§oNíØöN±j~Ã"ÀyG€{vþ; ü!Ð]î¾TDHL‰)!™q©ˆ`§; ÌŽ¶Ó ¦Gá )¼Žóu*|ÝŽß<˜þYà‡ßZ;õDÑl#ÎÜw ó:[©¯+dk¹{b#š©ãšécšÄdsãåëR„WtúË7žµÃùìƒë¦~ÛTZ„EO€û‡×kÍWA½ö5Ñ0s*“éojÒ±òqq»¼w¿'ã¼DSÌ$¦™ILMLMNcëÒZÝP„‡2t ¨’|4 >÷àšéýuE¼MX´øø‘Ž¥nÛÜ|œYÍÕ™Óš±}6‘“žû¬a˜,íb¸oC½ëXÑ·–€§Ú@ñÔ 'ÆqøÜ^ŽœÝËøÌ¹¹a¹!´Ü wƒIDz9L°€ï¤M{ûwVÏœ­Y€6`ÑàÞ×–tˆ'ùà/€`áµÄ¸pæ%‹È)=ç¹®`[†®g¨o=Ë{Öàqy›.Ûdt„Ãg÷røÜk:³gކ  \íÂ×3Ç^ˆÿM¥¼ßxh󹙦 Öî;ÜõnÑò#¥…þé¨pf—Íäa»è~¥—,½Œ«×¼‹5K/E1¯¡ÖTD“ì<ø»Žì e%Š®u­1¸ÒÄ,–GÁYe¨ÿðàšÉ§Û&h,Üs¨ó>%üw Ô½Îí±ßo#yð†¸|ø&®Z} ¾6D:ÊK‡ŸáÅÿ –Œäü• =ëM–\fbz‹ˆ`‰âÓߺdêÁ¶ [ N€»_Rn =¨àY?Ñ0ö†Íè^Ûi²e`&×­ýnZÿ‡¸[ âAÚN±ûèž}ýQÒV2çoº¡ïR“Þf‘±(ðm‰Dî{øj)aÅ´ J€»÷‡û á'·dýì$œxÎ"v®¸ž_Ù·‘?¸üCô†Û.g-˜Šòó]ßçØèEþ%+nraðVÁ³ZñG¯Ÿm³˜yŠ÷¼Ú"J=¬Îú%§„7e‘Žæe z;y÷–?fóŠëBÌú ®£Ïò‹½?!mçµ;¨z‡ ogQ•pD‰Üö­M‘WÛ.' D€O¼¾M)~HÁàÌÌ)Íé…†õº+xßUÅëò·]Æf`26ÂÏwŸã£ûr~† –½ÝEÇ`Q“1"Ÿ~{óôcí–±íøäká?á æ"Œï·yÍ.êž½aÝ­lÝxG[-ûV@‹æÉ=?b÷±yOý›MzÖ›E·*Å]·yú{픯­øäžÐV­Ô¿npŒ½³»m"'òõ½ËpsëaÓàµm“«xþÀÏøå¾âZa°ôŠ"ã0mˆüþß]Ù1ûùV¡m¸gw÷*Ë´wýàŒÆü­E² Ï¾Ã×ÉWßÃ@gM{ç ^=ñkžØó#tA›ÖÛ¥X~«p4rÄe›oÿÖGÛ!S[pïkK:RVâyà2N½`-6ö>xý¦Ë?·]/s†îÎ_}Çv}ËNåüü}ŠÁk]Ôv{<.ßíè5lùœ@…RÉtâ"\&"0úºM|Lœ+ð{;xÿµ÷ö÷ Kü¤I?” LÁð€éwÜà €áå0šZéßʾÜvåÇ1 3ÿø˜0úºM6}D¸,™Nü@¡Znµ\ܵ»óˈ|>{9©}-¯=.w^ýç, 7%<¥ÀåS¸|`zÊe€a‚2ªKO[ÐÄvª++!X ÐVsÒëS/ðäÞùõm6 -/(“J}å‘+¦¾Ð”Ë ¥¸kWèƒ"êÿdÏ“ÂÙßYdƒtnn»òn–w­©é½…UB.Ãý*—ñoŒîdÏég‘Œm)ÐN##wœ/m ìyæ™ã¬ÿÕk·rù%×9dˆ é„`Å…2#ÆUᥣ¿àׇþ¹(.K¯ráëR~ò¡G®ŒüCý¡Ì–Í ¾ëÅà•¢ŒïfÛvVBÝã”ü¬b{Ï¥bY×J4v¹×”„2À6ð.¯*¨;°âÖ4c±Sù ÔùŒ-"€qÎ ýJ<KN‚á&¤ãâ„bÀÊþÍ ÷n,ßJVB Ï {Øíwo(Cì7éZáÂðHYMpͪ÷ÐìÍ¥W:žÑžÙV‰È—î~Iš)[S H„>%šAÉ´Ÿ#'ó¥ßíòpíÊ÷ ±K:Ó§é6 t+»!ç´ëš ݰL.?t»ôdãX¬1LÓÅM—ÜŽ¡T.Ý"'5bçlÁD"ô©fƪiøðÎp/Èg³*+:*Ø)rúìòå[ xB¹zRgœ éè3è^áÂôÔ£îoP®Zöô 9ñÕi¡EÓÛ1Ȫ¾Kséf§œ´ÌõY ŸuÒº9h$ÉçEÓ‰8¥?v6_ú»}l¸¶(¢" MÏ‹@·‘±Ü›åZ…æÉèò)z†Ýx‚Ì1"¯znÓK¿ØYG àôMtJ’ÏÓ$4…{®?$"÷dY´M΢½rè÷`V ÂÔô ¹qûÍLØÖ‘ ù2*%tºð†TQÚøÜA6¼=—~ÚvÒ4oÊ={®¿ö…%ФìÄ{|ÙÒË—þî@?á•ET.¡o؃۫hÍKóѬ*`n• t-sá SÔ?°qé5½á\:ÆÇòZÁ—²ïmF¼šB±¹#Ûi‘Š8LͲwí’+‹"¦\šÞa7.8ÆZ«\³Ñ -U  <ø»Œ1 ÃdSɤm¶sÈæŽfĪaÜý’r#rkV?¥gò=~>w€¡®õ¹Ì7Üš¾a.·Ð¨¥?¿ÅÝ*Ð:™EÙt8öP6½†»7âsr陞Éw rëÝ/©ÚµÏBÈLß)Bgv %“kW÷nA¢eÂ’•~\ƒ¢ÞŽ–¸FcUmê–è^ê!Ðéh¥«{·äÒ3“ü •Й ¾³Ñð¯DîÈ’2'LÃdUïæ\OW×€Ã5×âmkA3PZ;Q¤ð×5àÅp96ÇêÞK1 ÓAͤq®I(Òp5ÐJ!êölufÅò…pyç%xLZl|!ƒ@ؤFTIêeU@{äW†Ð3èE‹Ûô²¢k]Á@惺½ÑYC à®Íò¬ab%$×¹ªo ¢‹¿w™…n›kÚР£Ç(ÍÊÞ͹tµR8J¸ü®i$V Í0”q{VÝê´#”RÐí_JØ×ƒ é[tTK2¥ Zdˆjc€î%b3IÂôô„‰¥§ :En±¡ŒÛê £!  µÜš«ÿyão¸{3"š`§‹@ÈUh¹¶Ï5…Ó…Úä CètªÑðê\ú¦y;@k¹µ‘hÕ­¶oW†\Üœ=×鬮è "JÓ;àËMΘg’‡‰ëäŒÇ¿ð˜‚|­ä?=]o´ÊË8~ŠWîvÂË„;ç8'“S|œùClX¿¶b¸þ ‹Ž.7©U™xÈhÛ|²nÞ¾]Û¶I]u`ïU¾•J“[ë*PŠo†a »1Íl7ïü8ÿcÉåçíÍÑ“Ò÷Ìòk6Ÿ8Ä¡ã‹Â£ì\B)OÆohÅÖ_RUØánÓ“½]DSS¹xfàÝ{•o%p¤žxÕMe˜ësœË”§þ_‚  w{¨Þsî»­ï xPÉÒÙ^¢t•Ð "°¶³!û€ ý×p÷ ßÈ„%ùpæ ¿Œfp&—FøÎ“Û!Óª¨¾€‰Ë½eÄÒS¹Âµu”a®§ÝÀR²ê=7ðtúûq¹Á4«Ž`7wýa×ÒÊ7¶ÃÝ›îÞÔ´÷EÎfÕG „Çû ³‹±¶µ€“P×þ„u@£×"™™¹:?Õ;ìëÍ”þZ2¿½Öõâ@¦.¨]¾î\:‹1²oÒ•‰2¨» "]h!çˆ.ÊM+Ðuï$ÒH­‰rÀçuS_æ_ˆU†Â˜5”?­² ËË>åñ8Sî2¢YDË>Põ÷ŠDóæ(Nýï6.X•^+œB37±"ú$>£O™–Ë“y¸p@ ©›õÛ¢¦ G_•‚˜=ŽBjv* TI7m/›V–žq4@AÚ#jª~ê…–ƒY#Dl@)NO%-QòôÌ»öOáZüPJ•t1b2Z"­lÆ# ”“æ¹Yðr°^êï ÖjŽ…¶£¦ã&ìƒhRÌ&ÀÅF@1 cž_\Ì)ýI™&O8À.ÐZÕýYšú5€iïËJ Ú骰´&m§™ÑÕ5iò®n)Î_¨ò@)…M‹x®”Ø$‰è$Ri§Hé‚:À´÷U ®ê&Àô¡äQ„t®Êhx"MR¦HI¤Â 5Ä… UáÓ#Ø’$¦G™´šX2vaýOzúPòh½2ÔÝ xf›Xïûž?¢.ç#Œ†G‘H¦è™Ñ'C¸jØÏà—“ßkê„Ë{ßÍŠŽÆÆòO¼ÎïN<ÝÔ !©*ÌèNaRg ’rŒÆX2…–üxJö?³­þV6¶K˜¨Ç$OHO¥2BDŸ d,¯‚N =6úå¦M -|rËà `ßÈ‹<üëÏd¦vIQxuO Ë ÖÐ-ÛÆ²-ìd–aêñFâ×l‹G ƒ¿°’N'#1Œ‚í¯gä^:ñ=(Jïq´.¸‰¾áyKôì’”¿G°tÊ©3uD875Íé±ñF¢6C+ºèœ[º s²Vžìð¥S5˜ŒFœõ¿ÚæÑzã àúÓñçwúGÈìn%„3““¤Ò^w~ÝbJ¦HÛ3øT7^#Ìl«oÀWÃg`D°ˆcIœ´Ä3ÝàþŒs¶uÓ ÐßbãúeM}g­xó옳ø&¯EF®?~žG*¢¡uÛ¶‰Qÿ”[NŽŽa(Uä”Ò$#¢—3¤d›xf%Ì|EM‚”L“ÓDä(19CŠ)D¥æ„“Ý_§Ùpoæ†Õ.gÙ6g&DZ¢ùêFDýS½ÓÁ³h|§PáQ„Bvá¢âØÈëË•j%q§‰# Sy2ïÊÏ”Íïõ3·•P©îlÅWF²–ùBáÄè(¶-¹¸4¤þ¡ ˆ{cOùâþ(Dœ”gÌ8“33ô„ªÛÅDÈì_¾ùN¢Ú½U¨dµ·ÇFF°âyëˆ&|±§}oÃËßùˆ$Dx"Û.MÍ Žœ;W¦§»õ®U‹*>3Q¦ã1R3½ÂÏ|D•åžMÚ%L~šÕÖ©ˆ£ŸNNŒ’Öö¼½]­s͉U!T…ž»Vº££Îw§S)葟6#^ÍÙ-<éù™¸Òà ‰qð÷ {ß<ÆÕ«êž²^7ZV,€ 0áØè9ã~›ÀR–çgÍxS4À㟞W"ŸÕM±s6h82r†³Ó“m/1­B»ã¡EØyx¿³ïâ¹|ÿ¯ùûÇ?=Õ”vnÓ6ŠLÛêK"ÄEœ=ï£#ÎdÁìÇÒv[›L­ j£üY·÷ä1"ÉÑç[ ™ü§mõ¥fÅ«ixúÓ±“ßÌ)ÑsmCÂN±ûø¡ŠÍý5N«¤}¿ÑÈ4ûÏ@ÛNZæŒ?øæÓŸŽlV¼šûÅDâk¸}ŸzDÃÌIMçjƒ£ãgêîgEwSƒ+‡VP@¡j–m³óØ>PN õŒ“N|­™a55FO= S‚úJN Œhì„3‰ñÅãûHZé6•ÿV5ÛóÛ}òÑT;!DG K¿úÊSHÝÓ¿J¡é”ö¸ã!#36}ܱvšgì"e¥Û`4;Víë ~õôŽž•I»ìŒ9á˜Ç¨Ùñj:~~¿$µðÅ,kccBrÊÑS‰(Oí)7dÜZ-Ð\´£äï:q€WÏA)HN ±±‚½€„/þü~©{H9´¤R»%–ø!¢^ÉvZŒ½a;ŸTSIÅxòÀ ÄR‰Öi€ÄIÑ:  Pì<ñ¯VR{ÃÎwúˆzå–Xâ‡-ˆVk°m›hDî%ì$Œìѹ$béOìéD Õ’îÓVi€t[‹â×Ç÷rpôJ9£|#{4v27êg!r££~åÐ2³öéâ;D˧òcÂèÞÌ"Äí$OÜÉDb¦ùA-2ZÑÑóË£¯pxâTÎnÝk“šÉ«~Ñò©§ˆ·ìsò-m×<ó¹ÄCgUYlD˜<”ÿ–@ÊNóäßp`ôM”4³ü;©ik‹´N;»Ø¥*8[§æM'fxòào9>}&—“‡4±‘‚þ~áág>—hºáWˆ–}94‹H_âþà9ïF¥¸`âˆ;Ëî¥Åæ7'÷rtê47 _NÈÓøG±²åÿÛ¯ÞË·÷ÜÛð§c³ïlF£áÕs‡Ø}æZ$'ìÌiÍÄ‘ü'tEx6º$yÃV@Ë¿ðûîµ¬Ô ÀjeÀòëÝx»ŠÔ4L®Y¶M½«Òâ¯>ÎËÇ7õãÑ×nZÍVÖ/0Ÿæ—o¾Âx|ºÈ?9)œüMºp¬ÿˆËå¹ö_ÿjz¬¡«@[ð®íÞ-Z©ç€Ë«X~£ wpnN/ tsóÐÛûêÞøbQÁÍËgòÊÙèY3œÒQáäóVᇧ#†ÈÏlK¾ÚÙÚF€[¶ûnødlÓ Ë®uèŸkŠ˜ÊàòþKØØ³š€ûüÜuLD8:}†]g÷1‘œ»P&6¢9ý‚…Îyiw>»-ñX»dl+¶nó}Å×s(蛋®Õ¥íQC)Öt®`Kïjúuï†ÖV¤ìoŒgïØ¢éxÉ{&hF^¶ŠwÓ>»c{âí‘ÒAÛ °õ‹þ»DÉÿrsÇ»/1Yò6sÞºi ‡-½—°:¼¬¥ãþõb"1ÃÞ±C˜|Kì’÷ˆÀ¹—m&]O+Q¾ã¯ã´EÐ,ÞñEÿÍJäɬ).5¼Ñ…YáSHn?—ö¬aex€.oS>ŸW7’všÓ3£¼6q„3g™o ÊNéç-¢g‹útFD©÷ÿê¯ã¿lµ¬¥°`¸é þ• y ¸<ëç )†·ºð„ª+á~——Á`Ëý, öÑã ·J\’V’Ó±qNEG8e,1YÕòÖTD8¾ÃÊÍ™ÌàAÝöÜ—ãÇZ$nE,(Þµ]u¤RÞ(òßÁ3ÜпŤgƒI­Cð>Óò@ƒÁ>úüÝøL>—¯á©©iik›¸$a¥˜NE9ått„ñäµ 7‹†ñ}6#¯Úèt?<êñ$ÿã3Û¤î žš'€RJÝø9ßE¤è›¸î béÛLÇ@l°ÊW€×ôâsyð›^|¦¿Ë‹¡LvŠ„$i%IØ)âV²l^5Ä1ôξl“ŽÎJc¥¾òüWÿEAâ/ dqÓ_z?(¨G˜µõ©¯G1pµIh°=3rE䔿ÌK6‰ñ9iUÈ]Ï}-ù !W),*lý|pYÚ²·+ø(/'î4Xv‰¿wñµâcÂémfN͸³¾ëv™Ûv|¥ß³i‹ŽYÜð€w“úšÀm³¯u]bлÑ$°Dµdை@ìœ3~?yh‚Ç4ò—¿þÛäë ^E,ZdñŽÏúoÖ†þ¢®›}Íåƒð°AxØ ´ÜÀhùЖmAä¤fú¸ã¬R ´”üÖÐÆg~õõ…iÞU‹EO€,nzÀûG‚ú äR#áå«B¸|ÍU VB˜>®™:ª‰œÔ…«tfã Bþ깿Mþ¤©´ç ®¹[¹Ý¾+C>€p =‰EPì7ðõ(¼Žóu);CËep)ÿùÈÐj¢Ì>fÖqáù섨”éM+µU^¯Æ¯Òù2 ŽŒ¯†Íx¦Q"QâœYÇ0·ÔWÊèZKc­¦[p¯.$A©¾³J WKi4Ê×zÞÊg 5@­ lÌsŸ*ñŒQæ}Îà½ÃïÁyðz½I&ÆV±~õ±¬[y8žõ«ÊDoµþDˆˆˆˆˆÈÞrç½·pí —rí—rÓm×05{o.î à #b1ˆ˜E ¦ÇÌpÎ0—>z8ð|á( ‡ï@QxÊ®£ìxÊŽÇ—_€œw¬œXφΑëNâÁëNaåøzýÈ(‘ÕÌÜß¹ö\qõE\÷ý+˜[˜J…<s‘b KÍ"¸æ#Îå.G €6Àð¹#çÒsqxŸ:|éét=Ýž§;æéö |éqÞ‹,ë­áˆµ'rÜ!OåÁkG·œÐ?˜ˆÙwÞs+߸üK|óÊ ¸ãž›rq 1¬&Äš,F¢8R›?¹àO­ÿ)pÎr‘O^ùoÒGÇàsÎùô8Ãp8œw¥ÃŽîXÉØDAo¼ èø0ÀA+å‡<ƒã7<ƒåcëô(¢@DDDDDîËíwþ€ ¿ö|ý²/25³)·ïBlÒ ¿Ð a°ÒO­þø´ïßyüáóª[¤;oi¿ÿ)pxgàÓê Îû ¸6Èa@ºcž±‰’îXÚ*ƪ‰Ãxä¡ÏäQ‡žÅбCô*¢@DDDDDFmÞz_¸ðã\ðµÿH…¿¥•þ&Ô„¦¦‰M àXÄ|j÷wíÊÿ`¸_.ð‹Tôû¼Úï½<îs@~<=Ç :¼KÅ~ú^~Ð à ‡÷~ð|ï=®£7QÐ/(:€Uë9ñA/àÑžC¯\®`"""""?ÝbŒ|é¿>ͧÏû·ßõ¢54¡¦ uSBM¤!Z ôkhþáÛ¢ß(Š´jOAšêïÒÇA±ïÓ€?_¤Ài%ß\{ €®ò;Ã;OQ¤¢ßÑ>ž;Šô9_ø8¤Ð Ó-èŽtÇE æ`ýʇñ„£~ŸcÖ{þ¿ñþ¼ÍÛî¡ }êºO¿^  ý4ÜφýœK«ïä}i/^Éo÷ñVúE1²úŸo…—€öó¾téñ‚áj¿W¤EÞ'þ~(Šô—xR7+¥÷ÃP ôy›A ÊŽ£;æðeš/pàäƒ9ãèWpôgè"Q """"ò“mó–{yÇûþ†/\ðIêЧnúTõu½Ûü,oÐNèwƒâŸAáÏ¢Õ~·hï‘o.ßÊ\ð“ï·Sý}i© o ðÅðïòmÛ¿Kmÿ87<) ðx×ô8|: o(ß?Ei¸Ò(Š’SÿwøRúž."""""?y®¹îJþöí¯äêë® þýAáïFŠ~çÀœáh'ò³øè>Ï XÇCÑøk÷#«ýåp•¿hï—é¾/Ûûnð5¾=) þiÛÏÃ-ïýÏÏÇQtŠÁv¢LÛœsÔP)8(º†+ÓфǬy*góW,ë¤ Cd?Pê-Ù3.¼èóüÕ[þ„»î½-µû78€Ôboäãú ÝçÒq~80òs‡ƒ)þé#9h™AŒ@ÎÜàqoékbÌ 8‡ypù/7ŸþnçÝàïLñ°3,=߃¹˜~fïÒ߉§,F,1'®vx<®¸aÓyLWwð?Ž}+N£ D䦑=àãŸþûöW²mj3UÕ'æ_³µ46(س\Í[.üÙà‡Gø¹Â(;FÑ1Š.Ãw ìŒ@>mÀ_ÖP«ÇÄ3ŽÙÈ¡+NÒ…"¢@DDDDäÇ×ÿûÀÛxÓß¿†…jŽ&4¤•sf€yˆ3ŋÂPü›anŒüÆ>ìðÎð%”=£;)ÇŒî„Ñ›0Êžá;© ßBáÌ€¢ã(»ž¢Ì³Ê4 `t[€s>m*î]¾ï¼Ë-ÿi €ÏÇúÂå¹>Ïð”…K‚ϧt|aŒwVóŒcÞÂQ«Ÿª FD€ˆˆˆˆÈŸ|ï›ø»·½š&ô±¶eÞE…TðcíoÝ.Õúƒû»År>Cƒ´oÊ®1¶<Ò[™XiŒ-‡nÏðyÕg¹H÷(ó\€²L+ûxË' ø ¤¢Þ¼Qä¢? ̃‹a°à½Ç»|L`žQPä0À¹» ÛàŠšn9É/ó³úéºpDˆˆˆˆˆüøx÷ûÞÊŸÿÍŸÐÄïÓ1xK,iå¿ýÅÛ¥n—ç ß?ˆ‘» Raß™0V¬‰¬Þ˜Xmi ó8ç1lp|_Y¦@À{ËCþr·@.æñnpòÎå-ÃÇ|;¸0ŸÐÎ¥ƒ>MLÝ Ê>ãU<ë˜÷pÄÊ'ëQ """"²ÿûÐGÿ¯xí‹©B•VÃq˜X,Ó¾þûûE|ŒLþçìò/ôn» €˜AÙ…•G::²|¥yR(á]ÚPteáÒ)eÚ÷_#mÿƒ“Ü`¥?­ü»‘©cÀ¹40°È³Ú0 … àz³ønŲÎAœõ°aÙɺDˆˆˆˆˆì¿Î=ïS¼øÿ¼€ùþÞΘuw©ðßµbÞíXÜïn@>! IAÀšE6oL®4#wäãË"·ï“Û÷Ó*Qú|ú€O.¿n—ƒ¸´ý¡(ÚÙ>ŸvnÑ5s”ÝÈŠÞa<÷!cÍøCuA‰(Ùÿ|ó²¯ó«¿õ‹lÙº)·ÁwÀFN×ÞÉ ¿Ý-ä‡sv»ÀA@„PÃÄ*8âã ##¾ô`Ž…'Ÿ,fÅè*˜Ú(ó±Î§^‚4ìÏ Vú†åp`0;€tšA´@¤¦»¼OѺìdžó1V¬Ò…%¢@DDDDdÿ±iÓ=œõ¼§rÍußÎ{å»8Ê%*þûû-üGøÞ-ݰ|ÙrŽxÐQwìñ¬Y³–å“+˜˜˜Ä̘›Ÿefvš›o¾™ë¯¿†fò|ü,c“釱è(ò€À¢Ìû÷}>Ò¯Èó Ÿ‹zFVúÓ꿵'´mÿ9hw7¸"‡4ÄØ@[ÕÇð°ÏæG¼ïJ]`"{™þ¯LDDDDdTuÅKÿïïqõ5©ø÷tpV-î0Øoû}8ü¯}à‡†ëw<ò'ð¬_x6O|ÂS8ìÐ#˜Ÿ¸Ï¯Ÿ™™æû7ÝÈå×]Èw½‡Íó?À{‡‡Es˜9lP½§ÌÓþqØö`Î N>h_›/r„OßÇò©!ºô~EG5]Ò[Ùpõ¦°vüxN9øÿè"ÙËÔ """"² Þñž¼òõ„sP<Åý®û;çrñk;]¹_ú ïÿ)G<è(þðw_Æ3Ÿñìû-úwæÎÍÿÍ;ÏýMnÞtÎ;bmøÂÓé¸"RE:J°€/].îó`?,µø;ŸOAÈCÿòуfiž@›%khšÙÜàè. Œ­ ”nœgý¯±â ]h" DDDDD8ß¼ôbžûüŸgzfeÙÁ¹â‡ÿÜ9Ìl÷ööo÷ù³žù?ù¿/{=9ìG~m›¦námÿñ×;hÌ[ÌEŒH4cn‹#ÖŽM ×ð•[_§ ND€ˆˆˆˆÈãŸÞÿN¾~ÉExWà}9,Æ·+ÌwzÛ¥P`é@ ý\ë÷~û%¼âe¯£,öì(¯C×<ŒüìF:±t$`Ç¥Â?·÷§@Àá Ÿƒ” xç)|œÏÝç °ö¹ÞçPÀ)É[pFl`nkÚ*ñíMäú-ÿ®‹ND€ˆˆˆˆÈ¾õ½ÿ¾Ž7¿í¯q8Š¢\´:¿ýŠýN7ÖþÁ@€£ñœ³žÇË_öº½öZñ §ñ³ø]pŽn/ iÔà ¸gä¸?ï†E‘>ç=.­ýã‹‚"ÿÞùÔ%ÄbúÞÎÀýYèÏ8ŒÀWn{óÍ&]|" DDDDDösÞöF6m¾ïËE«ô1Ú+öéñô¹]v!h¿ö؇Çë_õÆ=¾ò¿½3ó_óh¼w”]r‘žÿWîðÞ§Î\^ÝÏ«ÿEZí/ŠÔà]óEHîð®ÈUˆ³|ª@ ¦!ØÔ¿ŽKîÚ¨‹OD€ˆˆˆˆÈ¾qñ7.âcŸø×\¸úAA>Z˜/U¬§ûK‡Ã@Àv vxïyùK_ÃêUìõ×<ÖYÎx1EYPöe·=ò/Oúwiò¿÷©õßyK™ËŸ/(òÊáÓó}ÞPx?xçÁÁÞCSÃü¶t´áew½‹;g/ÓE(¢@DDDDdïŠ1ò¶|3ýªOáwlýß¾è_j…édžûû‡Ý‹;¶ÎxÒ™<íŒgì³×þðCŸÎkO (¡Ûõy%ßá)ò%iu?Ÿû—ÛýË< …Þç½ÿ¾L_ïÓ×ãÒ©¹¡—»|‘¶Ô}¨m–oÜù~èI‹"¢@DDDDdW\ôµ 9Í-ìn·¾öþæ,Ý9°ãð?€n·Ç‹ç%E±Ï^{Yô8áˆçPÐ¥ÓíR”¼+sAßÉ[ÒIí¾~Ÿ§ýN ȳ ŸæyK€w~XÔ;K€_€+ ZÚ €ƒïmû,7M_ ‹QD€ˆˆˆˆÈÞaf¼ç}ï ij _ã¾:†AÀãO=“N|Ü>Ž[ÿ4VMJ·ìÑítqƒ=ÿÎ¥P¤ð~0äÏåAiåßçÇ\žàRà œÏûþGVþOÞAÙÉ €¯¹äî·­ÖE)¢@DDDDdÏ»üÊoñ…/ž;(tw騿Ý<öo©0`TŒ)ø¥_<{°o~_Z1~0Gô ß¡,K¼O7çÚUþvÚ¿Ïùd€¼ç? Xä"ÏQ(I“|Š`ê$ð.‡–:€…Ç­sqóì…º(Eˆˆˆˆˆìyþ·2??‡s¶“iÿ÷y‹é¶»áÀö]tO:ýŒì}8tÕ£rС(:)i‹ç1Àû<ÀûÁãΧã 7¤®Ëûÿñà Ã{þy¤@ÑPCÓW®Úò.Œ  SD€ˆˆˆˆÈžsç]wð™s?•W¸ÝN÷ðßßð¿vÈßRÁ®v <ñ ?ËÚ5ë°÷bÝŠ‡Ò-'é´Öï œ+s¡ŸƒŸîƒSÒ±EQ¦ûø¼5 ¤ô%ÎEðm€Ìp¼‡¢€¢_BÕOݺpw.|K§ˆ‘=ç¼ó?Ë­·Ý‚sù×ä]æg1ßîãhÀÁ-²t°D(pÊI{@ß‹5ËŽd¢{`ZÁÏ…¾w.íûwïàɧzJ|zNQ”y 'RåSÚ£Ý` žÁ|€²„PGbæ×O}T§ÈPê-Ÿþì'ÓÞt<Y,eQÁo¹€Ýþ!·ô×8ƒ˜÷÷\þê•+WòØOÙã¯q¶¿™[7]E§[´§Ÿvrûs8Oæò„ÿ´ºÅ|Œ_Ì?išþÇÌhoäï›b=(òÛY–Þn|AòHÌ šwôÆ=7Í˃æžÊx±³ßdËïqÌ﫬Oé&X?vŠ.h"""""K»þ†k¹ø’¯¦j4º]žççÜHC€ þí‚Ü0WXÜ5þ°áÃØ°þÐ=þ ßåÒ?Éwn=—±r_vè=ʲÈmû|ZéÇ9ÌUÃî…ü³9si/?E^ñ÷y>ÀpEø6Tð8g[HGºa¡ïFÞHçràÓ×4},Œmâü»ÿ¥/ÁEÌE ‰Ä`Ä 1à¬Ã½U€ˆ‘»àÂ󙞞ÂQŠÝ]±TÛÿ°¦um½Ïuþpõß-þÚG=ò&'—íñ×8ÖYÆY'½&.pÍmçSZ‡º^À¢§, Ê2µë»"ýľpTý Wz\^¢w.pçòã¤Ê½êïüàõyïilàúø"…íçœéŠpƒo™ß4cvª!ð¾¬ù[Œ„1¤9øØåÌÃ7r슳u1‹(YZÓ4\ø__Î%­[¼’oKUðKØþù¤¶ô…lŒ>üà#Þk¯u¼»‚³O}½øe|÷öÏá©Y˜v„ÊÓéeAYøÂQùx¿|_䢾ޗø¶ÍßçcÃã-¯ø;š8‡YH_ºÝû5 r…*„`Ìl΃¹ð7bcË®£`œ3~+[­â_ä¾h ˆˆˆˆüÔ»ó®;¸ô²KRåi©îÇ®œ0:Èï>Žþ[|*ÀÈ0Á‘ï·áC÷êëíu–ñ?Où;Ž[Áæé-¯ð½>Uݧnæ©CŸšPÓĆhËÿ9 ?²W?mÈä–þ¶xOŸ6œsÔa#¦9>7 äãÿ|žüïŠÁ·Yôo£©Œzêy£š_ºô¹…1Î<â¾FÅ¿ˆ‘ûqéå—°ió½`Žm‡#üvv¤ßèçÛçÄ&`q¤¸ßÙm$ ~=ŒOLrôƒÙ믹×YÆÙ§nä¡ëŽ&4t'\YQWMX ‰}¢¥öûjÌ"ÑÑ"8KÅüÈà@È'æ †ü9ˆDúa:ÐNû¹ƒýÿŒÿ1@¨¦‚¦uߨòÜ…Í|Ÿ?æyðót‹(¹ߺôbŒ©ZµþûX]AQbMƒÅ¸Cq¿¨k`ûKÀòeËY¿þ}òºÇ:ËyÞ©çðЃŸNh ; ®tÔU^u¯bùuÄÔ `¹ ¿3¶ôöîÇ6ÀáˆVÑX:U`qñ?h ß!F#ÔéÖô¡š‡j.¿OB¿Ë3?‡Çlø]À" DDDDDî_]×\qÕ¥¹ wƒb¼½ ¶lwÛ~Eßæ+ÜŠµ,{ÉÛéœüóØl½hóÿŽKcãt»½}öúǺ+xþi9îà3‰£7Þ9šÊÁÑÔ†EO †—ît–Ÿ™ öúG ©ä#¦ãú`°ÿ¿‰}œ÷ù¨@F–þ󇑮ˆŒPCÓ@݇jÞ¨ÒhF û]ÎzÔ9œxø¯êQ """"²k6m¾—ë¿wm*.óžÿvÕþ¾ZÿÏ1#ÌU¸°òÞDïħ0ök¯¢|쓇!Àó–jÿ7ƒ•+WÑëv÷é{0Ö]Á¯œ¾‘ãÖ? ŽîDÚ£ßT`Áš´*Ot˜9,ļò?H1\:=!ŸÖÿÓ:ßlÍûÿÛ $ÏYp6˜h9l‰MºµmÿUª bîð®Ç³O| 'ù]¼" DDDDDvÝ­·ÝÌÖ-[ÀÚÕmÛ®P¹¿Ôcó5~åðÒtñ8Øz7VŽÑ}áðy"q:íŸo Ü l•+WíÓ€ÖxwÏâFŽ=ä ˆ>‡FSGBhÁ1ËAļòoÑò ~Þ÷Ÿ>D‚UTqÞ3ìõ·ô&º ïÇDo%Ï?ý-{È“pºã†ó‘Ð@,Oç·4¨Ï,yψ Áš4KÑ"F`!·ÿçÃÀཎšt«+Òì~úcêBˆÿù~¸òË—]u.Z"""""»ïî{î ®OEþ⛵{øÙ·?_ã—­bÝËÏ¡<á‰ø™­Ð4FÓ±iˆ!µþǰsñßbÃ|½ ÃÚ€´m ä÷·N…~hRÑßý 5àRÛÿ}¨àÊ/”c_x ãž{ïe¡ß×Å+¢@DDDDdw€>Û¶mÍGÛ¥-Û¶ÿáG̈ó ~ÙJ~ÍFº'??½“WöX“‹ÿ*Dê`ÔÁhBZ 1^°*ö±ÞrŠç¾wìÏ`ó äÁ€6r n?{fO½m†.ïÓ›„NÔÞBzMCŒy8`l03ê0G?ΦIÿä #‡*Mm4!Oú_HÇý5}#„Tü›ÁW?\rÅy1‡÷.¿1Žé™iªªÒÅ+¢@DDDDd×ÕMÍBµWâm§Ï³~À/_‘ŠÿSÏ„™­œ¼zŒµƒ•ÿTøW1ÒÄHˆ‚åB8}Ü0Ëš9¬»œòÙ¯Às6M´hûÝ{Ô+˜Ÿ1>~Î ß»¼ÏØ2莥B½ "MhBCˆÃ¿†Ð IDATa€ýf‹ÌGŠÿ k´û×ùch†G0~í#E*þÇ® õþçýý…ŠºntñŠ(Ùu1Fb‹ŽáÛá9ý€›\ÁA¯}+ÝÓžŽ›ÞÊ)«Æ9°S0UGú±Ð¶þçöÿ:FBìòç,DêF&šYâØ :¿ô§¸£“B€AÒMØ¿ŠÛ¢(èô%À´!‡Qögðõ¶ež:G„Š•¡aºJÅ¿TuCÕä•‹Ô!û³´¿¿=ö/íõÏEˆD‹¹®O­ýƒû¹ÎO!@î)ž]û¹|L`nÀB›ÝŒÕóÜvï/|Û¹|ë†}7ðŽ;ïbÛÔô’{þw,Öß:cÆ–; .?o_8ŠÎh§ÀȰ¿ÑÇòß±ýc†3õˆãu€ˆ‘û×7ã•wLñ‘-sôcä°þ<Ë«>SM`¾ ,Ô UÓP…&­ü‡HÓB ƒûíªÌûùC ƒ¶‹iú¿EÓÿ‰Ã€Tãç–v#oˆƒâ?Л Äõ~ænèOsÛ÷ð›oþ$—Ýpû>y¿®½î{ÌÎÍíÒÀ¥B_:Ê.ù¹;yþÈLöïØþ±t»%;ù±ºˆEˆˆˆˆˆÜ·ÊŒ—ß>Ň6ÏÓoŒÃæfX9?ËìBE57K=7K³0OÝïúM]BCýa0ý?XÄÚ`¤+ Úð¶hÏl€Tè»v@Œ8‹1ÿ,V÷¡^€PA7}7¶0ÅmwÜÉo¾ñ_¹ü†[÷ú{vãM7³C›ÿöCGÞ/· ·E3ß°G>üaºE~¥Þùi(þÿøöi>¸ežØ™ÚJwfšMuMŒ ÖÑó%]_¼'ºœ§‰ÃÅy ad•?-ÿ9D,¤¢?FÃBl'ý¢áF:Úáyº]nˆXÐÔ ÖT© ,6øºO,ÇùÁmsüÚŸ¿Ÿ¾ê×xÔчî•÷,ÆÈw¯¾v»â}”-`˜-~ÖÈŸw-$-þÓöˆÓ*¬^­‹YD€ˆˆˆˆÈÒ¦¢ñ'wLñ¯÷Îb }˜æö©in™ŸÇú B*:‹.®cl(#˽#øæ‹<ÍßA$­ú‡ve?.ús¬ê§U—ŸC; øÃÚýþm±›÷ü±Ð¢Íç‡sò€A=á¹åö;8û5ïæŸ_õœü°#÷øû¶uÛW}çê].Ô— ÏrnÉ ŒÈÁÁ H&rÆ“N×Å,¢@DDDDdçÞ½yޝl™á¨fןê9Ìú˜«q¥AáñEÉ‚ó|¿ßp›/8,DºEMpF„t<Ÿ¹áÊÛÞóÊnão ööTmPÛèÊ>`ô$Ëgšsô¼qغ¸Ø¤-Ö@-¬=ø‚…&ò×øm0—'ôÛpHûúC:÷/µþGÃYyÉ?-äÇáßïHÃí\ûsÅ4 ÐÅÈúU|ì ¿ÍªeãÄÁ‹km‡Ã0úu`åäØß¾úµK¨ªšÅÇþ-®Ñm—Zûï'¸i¸bäÌ3žÌÁ­ÓÅ,¢@DDDDdç*wmîõòn‡çµ«¿}33eɲ…¢ó.µø·+ú!¿£]‘<( .>Ò/¦­ÃbØòLjgØJútz®÷Ž•Ë&÷¬_U|ñ‚ÿºß‚~q°kEýýCE ï9ë™ÏÐ…,²è‘ì9GÌÚ±/™q±®M>–¯Iúc‰1C ††·¤âØ ãH‹ž@h#a‚k§ßçV{ç[ç*¾{ÓØûpÅ•ßáÊow‡ãøvlÑNï=p¸mÀýÐ?ƒ!FýðãyÒi×Å)¢@DDDDdÏ9rÙ8Ï>bà˜éŽÑ4‘X×4M 6 ±Ä&ÿÖ„áôÿÒ‘€ù$)úGÿKsl0pÐBïS—A[HGç¹òÆÛ°÷á£ÿ4ýªºï'-yìßR€ß¥``ø¹Üa³ÀóÏ~6ãº8EˆˆˆˆˆìY/<ê`Vv BQ2Wtˆý>V×ĺ!„†›´Âo¹ý?ohñ‹1,*ø‡'¤ûÎH§08ý/ï Hƒýð].¾æš÷ùë¿é·ðÉÿ8÷¾ ÿ¶ø_²+€Ý <Îù‘Ïù|übä°Cáìg?K¥ˆ‘=ïa+'xîa‚óãã©Å¿®±Ð`uƒU) ˆ¡É+þ! ü q¸êc>0s…ïl¸%À±ý0ÂTøšóà \ÑáÒïâê›ïÞç¯ÿï}?÷Þ»y÷¾hgÛî7pK>,,ò‚_~ë>H¥ˆ‘½ãÅ9˜JO]”,ôưjX×XÓ¤UþÄfXLí1 n6¸™Å´ò?ÚþoÎåC]êÈ…óLù÷¯_½O_÷Uß½†÷èß`d…¾]™ÿ¡¥ÂÎH ¨B`ýºµ¼è7^ ‹QD€ˆˆˆˆÈÞsÌòq~ó¨ƒp!Ð_FÁú i(`Ý@°nÄù£Åí\€íöþÇ82`äxÀô‡áq®ÈÅ1˜+ùäÅ×sï¶¹}òšçxÍþ†™™ÙÑJ~¤Žÿ!‚€] Fn1~ÿ·_ȇ¦‹QD€ˆˆˆˆÈÞõâc×sôDsž…Éå4U…5 45¡®‰mÓÀBšÐŽù1ÏH¹¼ÿp2@lKl‡¤A€ÎÒé7Ý=Åû¾tÅ>y½ç¼ý]|ù‹–ªÒ¹¯iÿ{:¨cÃC9Šßyá¯ë"Q """"²÷­ëðŠG=BCŸ¤ét óXSA¨±¦Nsš&¯ü‡¼= þ± ˜?¶·ÁÉ‹mgétç0çÓh sžw|ö[\{ë½{õµþËG>Á›6¾cŸí¶»ùÁ ¿íüíÎQ€¨c$ÄÈKÿàw9`õ*]„" DDDDDög¹ŽŸÛp„H½|u*îû±©ÓêˆuÀêBL·Ø~ ÃÿÑ]6èÚ­íöÀœÃp)T0Ø4ÓçÕ¸€~öÊküðÇ>ŽâõTMþœ?Zðß×Q€;~U0£ ?égxÞsÏÒÅ'¢@DDDDdß)½ãµ'Åê¬Ó¥^¾ŠØïû¡NA¡ÁšaÀ`[@Híÿ„´E`p`lLƒS9œ×ü'8ÄsŽó.ÿ>?sé}mssóüÅßläÅô™ŸŸ©ÆïP߸: G?FÆz]þâÕ¯`b|\Ÿˆ‘}ëø–ñÚ“Áêš0¹‚Ѓþǽ›6ýH¯ÇÌøò^ÄYÏ{!o|ËßS×ÍýÔí{# X T–¶MüÞ _ÀO;UÈ^â,ýÙ‰&¿qî¥|òºÛqUMyÛÀ®ì+ÒôþÁÚšKe­¹tÊŸ‘æäû˜å-6<°=*0¦€` „ÔA`E‰ùW-pÔÂõœ~üwê)œx£9xÝZ&''îóçïWß¿éf.úú%|þü øÒ…¥¡…?ª±”p*ƒ~Sqâ£Žç Ÿø0«V®Ð'¢@DDDDäsóÔ?÷¡¯pÓ¶y˜™Áßy ÎPtð®_¤–ðq`†#Ñûþ1‡íŸ-ÏÀ"f!!bíI®Äîú qwÕ®áðÃç¡9ŠÃÝÀ«WÑëv™œœ`ËÖmLMM³mjŠË¯üß½öz¦¦¦Gj÷½TìÆ÷m¼£ßÔ,ëñÙ¾ŸÓN9Išˆ‘Þ—¾¿ò±¯1]ÜæM¸{îÂÊ®(Áy Ày8œùáH­ìßy ÌNCg Ë[pE*ÆÍaær‘?¨°w´sblAoØ.°´òOÄû_ø¢ SØÍ—b÷ܘ‚„¢dxßn• fP–ÔE‡ª®¡îó¤Ç=–¿ÿÝÚ÷/¢@DDDDdÿ4S5¼è߾§¿s .DìÎ[`~:½ÔŽï| ð¹ðOa@*ò`X$0ì Ùà ò`ÀöqGÄ;OQ”8_¦çn½•xûw±©»Ò×ør¯LîßíòÁ ¼ƒîXZùï÷¡^àØι}?G~˜.("""""û¯m /øà|ñºÛ iRÐ_€²‡ys´À Œ#uq„´ºŸ‹d,ohƼE éÏíÀ@—ÿìÉs|‘¶„ 6ßB¸çFâô=ª4›Àùì™@`tÛÀ’åDûzœÇw{Äî8UŒÔý¨û²ö>þþwrÒ Ö…$¢@DDDDdÿwÏÌ<¿üO_àë7ÞÕ5Üu+T}bÙWæâ¿ÈŲcpT`4,wì¸=`©[ž@Ù `)ÈÅxQ”E+ \h`vqË­„™MÄù)h*ÌB:²°ýyÜžÝ*`1⸢¤èAwŒ GUU„ªMÍš•“|ä½oçI§=Nˆ‘·måWÞûy.¹é.h"vÏmØü”Ýtl_^wø8—öÿㆅÿ¢âŸí òÇ8|^ 8K#ÝÞ{œ/( ÷E*ÆC…U³„™ÍÄÙ-ĹmX šB8—~>;t ¸TlW5Xû¿£á¼ÇßéáÇÆñ13úUE¿¿€5Ću+Wð|3OyÒtáˆ(ùñsûÖY^øOŸç?¯½-µÄo¾›Ù–ò•œØÌ¿Ž×n0ì×väVúíO m p¹+Àççxrg€wxïñΧûÎ¥ã -âš kúX–¸0-Ìëy¬®ˆubÀÚ¿³ A@úèœÃ—|§›>v{øN—ˆ£ ª¿@¿_ÑÔ„bÃáë×ñÏÿwœ~êɺ`DˆˆˆˆˆüøÚ43Ïï¾ï |úòÀ<ÌlÁ¶mJ…sÑMÛ\‘ƒ€¼'Ðú?´]X®½#ÎÜðT‹ÃP  ,l·ä À‘æï9çÒÇx†fMúÞ1`M…*ÿ]š¸"õ¸ÜeÐþà†B ©*ªº¢ª*š¦I[šBÃ)'<‚w¾ù¯xøqÑ…"¢@DDDDäÇßl¿æå¾€wé²TèÏÏÁ¶MX¨¡ìáÚÉüí Eêêwy>B7(öc>= tŒtSqoqpZ@êðLÅ¿áÈ–~$×6ÿn-ÈݤSÌ ,c †@lšÐÐÔ5UÓš´ÚO]c!uüÒÏ?ø»?gíèQ """"ò“åíç}“×}øfú „¦7§¹E ¾ÈƒøŠí&ô³¨ å£ã]ž `¹@w1¦À F\Þ>àݰC qPÐ;b˜¾·kÃGê"X´ u˜,¦A1BH·Bȯ36i¿¨!^ú{¿ÅŸ½â¥Œõzº(Dˆˆˆˆˆüdúò·¿ÏËÞ÷9®¾ùîTÔ/Ìb³S©µ¾(ÁµA€Þ47²% ]å_ ä­1.ž`apß †ŽÈpËmAF?ßž:`#džRR€Å´uÀb€‘Âýºù‹W¾Œ_ÿåçèBQ """"ò“ïÎ-Ó¼öC_䟿ø-À§ò¹¨æòoãðù”€6°Ô¿è—tÛ¾ø¸ýL€¸¨€ œF„ià`íþí×,ä•‹ÄS¡oa0Àòª?M5˜!ðôŸ}"o|í˵ß_D€ˆˆˆˆÈOŸ}í;üå‡Îç»7Ý8hX˜ÁB? økz?2$0rßÈ1°“bØ øgð˜[ôü´Â¿èëcš%`1¦bßòþÿ¶èoC€ü°À!¯ãO~ÿ·ù_>ÝnGÿè" DDDDD~:ݽu†s>ý5Þ{îÅlšJEuSC5‡kªTôû´%Àò\—æïçßÜó–€ÁÑ;ë ®úÛ’[F»ÚB¿-öÛ!ƒ©ðûíý{ý‰ XdÙäÏγøÃ½€‡s”þ¡EˆˆˆˆˆÀU×ý7oùÄWøÄÅ׳0¿‹êš~j¥o÷ÿæ¤Àµƒ³öЀÅ{öf#slØÚ¿ãJÿ°èoïÛà±0ÜçòqD:’ç<ã)¼äý'žðhýÊ(ùéff´¿r;7,ä¿~éUüã§/âÜËndz>·ÓÇkšüA `#Y€-JhùÙ¢y#ûþóÊ â¢UÿÁý¼¯8KÖ¬ZÆ“Oy Ïö39ýñ§2>>ÎÂÂÎ9ʲÄ{?¸µAÇö] "¢@DDDDä'B[ð·…~¦iݪª¢ª*꺦®kœs4MÃM·ÜÆW¯¸–‹¯»ëïšeºÊ¿¦‡Ñ`¤Hon?`‡¶ß÷Ol·äUþAl°ÈD·äÈ ëxü ç´“Oä!ÇE¯× ÓéÐétèõzt»]:EQP–%EQཧ( " DDDDD~rŠþ¶ðo?n_ô7MC]×TUEÿÿoïÞvÛ¸®0ÿsàA²ŽvR0’"EÓÞhŸ ïÿ½+P(šâͬ9œ™Ý‹Ò”ì^Ešôû€)R$D^iý{íµ·ÛC°¿¶Ûm’©sàææ&ùû·ùßþš?þímþüú!ï7C†ÌíõûýùÇ+÷y²Úÿh`>þG3uÌ¿_—!çëe¾|y_õ*¿ùÕ/óó/^åúú:UU¥iš¬V«œœœd¹\f¹\f½^€¶m]OC[à‡Õú à?+þŸÿî†!O×ßö…ñv»Ív»Í0 ùüê<¿ÿí×ùÝW7yýæm¾ùç›|óÝû|{³Í÷}n»1»¡dWÊ¡ û€`.ôËþ¸À®!uU²¨§ëÙj™Ÿ]æÅų|ñòó|ùêe^<¿ÎååeÚ¶Íb±Hß÷Y­V‡"ÿ¸Åÿéç?þùøóí¿/Ý €ãb¶®ëŒãx(xŸ^MÓd±X|ôºãBzo†t]—¦ir}qž³“u~ñêó<<<äöî>·÷÷¹½Ûæ»Ûû¼»ÝäaÓe³³í§A}U“t}I]—4Õ´uÉ¢®²^¶9?]糫³<¿:Ïõåe./Îsyy™ÓÓÓÃßÙ¶mÖëuÖëuV«Õ£Vÿ¶mÝošæÐö¿Ÿ`à'ì ÞýJÿqÑ¿ßß÷ý¡pÞív‡Ûýö€õz®ë²Ùl²Z­²ÙlÒu]rss“ͶËÝf¾î·éºMNë’õY›ñ´N)%Ýn—¤J]WÇiµ½©§Â»mÛ$%MÓd¹\¦iévCÞ½¿Ÿº y‹ÂKÑIDATÆäjL^<¿ÎÕÕu..ÎsrrrØëÿ©ÿ}ñf€~’ö…ïþH¼} ýñ`À§÷û¾ÿè±Íf“·ïnò×ßç~x“¾íÓ/Æ´Ïš¬ªU'ç†]úݾïÒïvÓö‚qœæ$©«:MÛLÿô/i›6íÑྺ®÷W«eÚv‘v±H7Ö¹ë†,û1«u>{ñ<§spvì‹ûO­ø¯üÿ; €ÿ’ã=ñO¯ýóÇ-òÛ®ËÛ›Û¼~s“×ïnó°™N Ë ¤”éçqL]W†iß}ß÷IIú¡O)I]Oç6óÞûªª“”´M“º®RWUš¦žB‚¦Î¢=jßÏôú¦nÒ4uNOÖ9?{–ëË‹\œŸNxTTÌ…¾¢À“`à¸PÞv»¼}›7·›ÜovÆé(¾}‡Àq`PJ2–iÖ@JRÍ-þÃPÒ4u†aÌþ­«*©2m˜æ$e,)e<UUeúõ©k¡®ª4u=¿v&LÝ ËÅ"—g¹8?ËúA >¡ë‡ÜÜmrû°ÉnàWÆ1ü%`_Ìc™ïOûû»]Ÿªª2ŒcªLÇRRÍ!AÛ6‡Nƒºš ý9zÈ8Ži›&M3M3Í.˜Â€ië@}ô\•é¶Ìï_UUÚ¶ÉÉzê X΃ ðÄ0޹ÛtÙv»ìúa>¡¯$)éû!c3 S—ÀXJƱ¤dZýï‡!ãXƒþö+ôã8üSÛ™·äðÜT¼Ïü\Ð'9„M=… )9 õü^MÝL·M=?ÿáx¿Õr™õj•vž7àÿ^)%]?¤ë§Õý2?6ŽÓ^þÌ-þÃ8fœÿMŸŠÿ…|R’L«ÿ‡Gï_UÕaFÀq@5¯ð§$%%Õ|Z@Ue.ôsXá¯ÝÇ{ûçî€ý–€GG VY´‹,­ ~Hµ¯€@  €@@ €@ €€@  ÀÔ¿óäB¢Ôi3IEND®B`‚openxenmanager/images/storage_shaped_16.png0000644000175000017500000000145711636446664017563 0ustar rrsrrs‰PNG  IHDRóÿasBIT|dˆ pHYs»»:ìãâtEXtSoftwarewww.inkscape.org›î<¬IDAT8U“MK+gÇÏ‹i&“7MlT„(*MA.]»êJz¡ý]¹ìgh?E¹;¡@Ü\Ütíò‚…€F&F%sQ‚ÄqÆI&™yžnînÎúœßÿœÿ9GXkù6ÞÞÞðãÝÝÝîËËË.@½^÷¶¶¶<àÊuÝ…ñÐív7¢(ú»Ýnÿì8NÙ€µ¥I’DƒÁàßr¹üÇþþþg pzzú¾X,z;;;¿ !ÊQ‘$ ÖZ„L§Sâ8FQÞÝÝýÍqïììì °²²òAá„a˜«j­B „ MSŒ1¤iŠ”ÀY^^þ|Ô_æþ>Š¢¼XÖš$IȲŒ,Ë0Æ`­e<·òÆã±–RR­V MÓ­õ< C†Ã¡Î=HÓß÷év»¸®‹”­5¥R‰R©„Ö¥µZ~¿çy¹ˆRòúúJ \×À÷}†Ã!¬®®¢”ÂqœÜà|„/»¦Z­2¨T*L&æó9Ʋ,ãòò­5…BÇqY–Ùáp(Ç¡Óé`­e}}ÇÇGŒ1H)iµZ‹Ez½aâ8ŽÉJ)Ñn·QJáû>DQ„1&ïàùùc óùœF£Ál6“9`6›¥···KÍf“N§C†ùõYkYZZ¢^¯S(H’„ûû{”RYn"0ßÛÛÃu]|ßgss“R©„)%ÖZ¬µA@4›M´Ö³Ðëõþò<ÏÄqÌÆÆ××׌F#â8&Žc¦Ó)QQ©T¨ÕjŒF#suuõçÂ3ÿ´¶¶öO»ÝþáððPAÀÓÓY–¡”¢Õj‘$IvqqÑ»¹¹ùýäääÓàk}·½½ý¾Ñh¼BìcH’¤?™Lþë÷ûÏÏÏ“oóÿ– _H–PÁÆIEND®B`‚openxenmanager/images/usagebar_6.png0000644000175000017500000000066511636446664016303 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTE¯üvmñ ®üv¯ò}¯üuA–¯æ‡B‚A‹ mè¯øyC~¯ín×!nà¯ê…mßB†A‘vvvsss¾¾¾çççËËËãããëëëÜÜÜqqqßßß~~~ÄÄÄ‚‚‚zzzÙÙÙééémèménØ!n× ÅÅÅ¿¿¿êêê®üunßpppÓÓÓmò B~ÌÌÌÿÿÿ µ¿_2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïwIDATxÚ¬ÑI‚0Ñℳ ¨ š¤µãý/gÜ÷¢Ôæ ðí%Œ,ÖÆ *cÆ"ØEŸP¢àòþ—‹`Ú4³º*¸yðþ(‚­[9·Y(HŸ¡ëY“®íÚåZAö íK0Ï™y¬€èDDwô3ü'ÀözM‡ ÔÙIEND®B`‚openxenmanager/images/storage_broken_16.png0000644000175000017500000000107511636446664017573 0ustar rrsrrs‰PNG  IHDR‘h6sRGB®ÎébKGDÿÿÿ ½§“ pHYs»»:ìãâtIMEÚÃ^ ½IDAT(Ï}’ÑŠâ0†OÒ šÒè€VDQéO!ó30W>˜/2OÑ kA,•V¬Mk“6{‘ÙÙu÷Üœ‹ü_òÿçi­á¡êº¾ÝnœspǶmŒñ£@)µÝncÍf³®kÐZ[–eYžÏçÉdBùâ8B0ƪªRJY–Õh4BeYÖuMÁ_.Ji¯×Æ!pÎ…÷û½,K)¥”R)¥”*Š¢( ­µ‘Ȳ̶mc!D¹ßïPUUUU&˜Ö:I’/àt:!„c¿“)¥À˜6·pÎw»`sE‘ïûf&„J)¥”bYV»Ý‚`³Ù|Oi¹\G¥”çyÓé4Ïó8޵ַ7ø«ˆiN‡1–¦©ã8yžK)_>>àYa“i¿ß‡a躮ÖÚuÝV«õ(zÍó×<ÿ0Æ£ÑÈuÝ(Šc×ëÕìîŸ/(¥Öëu’$ÃásþÇgy`Œ=ϳm;Š¢Á`@)EýÇA!úýþjµJÓTñ(úlµ>¥Â0ŸÏ‹!äp8Ìf³n·kÿ44zt,¥ô}?˲,Ë~¼¿?µô ËLWÝIEND®B`‚openxenmanager/images/prop_advancedoptions.png0000644000175000017500000000317111636446664020501 0ustar rrsrrs‰PNG  IHDRàw=ø@IDATxœb` 1ÿÿ¢9ÿÿbÄ#dž¤F™AIý†§ 7˜ôüA7ÿÿÂe©<ƒBTT”1ƒƒÓñãÇ×ý‡‚ãǯc``p‚ª“suuÕd``Ãæ`ÿÿbÁa¸öªU«\~ýúõwûöí‡Þ¿Ï#%%eðíë×ï RÒR â œ ,°aaaaÞ½{÷>†› ß ¾dÿÿBGì nsæÌòû÷ïÿo^¿yýàÁƒëË–/_ùó×Ï?¿ÿüþ÷û÷ï?ýü·bùŠ»÷ì^÷åËçOß¾}û~áÂ…ƒnnn‰ ²ÈÁÿÿTϱ € @ÑObŽ/¡£d#W¢G0q0Z+¸ÊFƒ­nðÞ·ä€9¥´´Öv Þ{é½ßf6™ÙOcDU¯œs€cP뺕Rà€ÿÿB"f¾½{÷ž÷õõu¸sçοdzˆ‰‰1())1ðñó10üg`øôéÛ7oîÞ½Ëùýû÷?ž>}ú|Á‚/P#ÿÿ\б €0 DÑ+@©½€½€Gö:d ‹“LË‘:„˜àëý`U{ï§»’»™AU_â "´Ö@r›sŽˆ82ópÿ?ÿÿ4Í¡ €0@ÑOA‚ †³Jš°KEg¬è.8 48š8P¼ÞTÀ ªÚ[kû«”ƃˆs.)¥-Æx„&ïý*£tªÊý<­s®j`N`Þÿÿ4Í¡! Ño®<ì•Bé@PˆÐA0“3œXûö=B-%õ>æZzrf73'¢ xo‘ˆÚ¶í"âÌìªzƳԚ„kâÿÿ4б À Ñí÷ y õ±6iBqí+îþj€¹û0S¯:-³DfXÀû·"bîÜH¢ª8‡&©ûó À®ýÿÿL’1À Ä\t üÿ¡L,$G:´•º[,ÿ¢Ø;Zïsv"iøVš’VæÃ;ˆˆVUãurÿÿT“±À Ä~‹”€…~G` F1%ýû(’")ÔªÑéW¼Ö6æ´%  ¹cïÉ‚g¬ûå"Y"6äŽÈ„ä0³Õz_çÿÿ‚åvhDK655Y›››ÛËHË0ˆ‰‹1<}úôý‰“'6oذá#C@@€™¹…¯Œ´´à«W¯žwÞÜ£þþádddd=räÈ}111žððp+&&&&F†ÿÿþÿ_¹rå‘—/_~QQQÖÐШ©©YÿöíÛ÷ o2ÿÿB/ìX åº!ƒ˜¡äù ž100ˆmÞ¼9MXDXˆáÍë7ïüüüf200 vpAg\Æ­Ã…IDATHÇEÍiTSw†ñ÷s!IH ¬DDAÑqE™VÄ…ZA—bE‹¢8ÅÁAí(Vp)ê N­”±Ö]Ô.E«•Ó"($Ê–•Ü›ÜùÐsêóñ÷å!ÛvïžÞv.È&½”&î6E_N¾-W\p Æ»»¡°æsn¢ CaÝ›…á!1ƸüñUò“>9òÿ–N³79’{ft¼RW¼:Q³°¬ò©)Žw–ޱqþ “Rw\§üÙÑ–í}+9Œ¨$q:4à€="²QµõYåQ€Ç§š$ô+JÈà.)cÈ~7ªzúÏk½3G»Gùðä¥ÿ ¯÷èĊͲ‡Tévð©8wãm­/ëÁ¶kV¿-5mnz5Pôæ‘zß‘´keyéE¥) -EÍÇ©kÏ??’»‡5Ô)Át²Eõ…¥ZLRë­3M7¸³Žy܆ò s›p4fÿ¥E¼×eó$"œ¨¸ë‚"?ô“ ú8Ý%÷=—Š¿·EZ÷á ‰eÍ…ðã~Ôçd±Z’:¸¡' —¹^Û?­õÌùŸW®~ÑïóÄll‘­M±£š® –j'¡ °}ÝÙ%ÝK'KsWEýäAUó\}×M\j¿ÏªI»Ùo)ƒ‘ªg¾7»CN¢,á¦:´’e&›n †ÉXC¬†_Ô#µ¼ä›ŽèÍ”ŸýCÆmÈ]œºÛq7ÙS¾>N~&!#«Üñk_üÅò«è&ÉQiþ-BÀ¹„Ji—Ì£Ãù5ÁÿVéÜÏß'߯Žwêv®7Ó üùü§9æ/:Qapf ì%„>R†Û­¦ò¹.pæjS({Ävs¸Àš X÷÷³Ÿ[[/j9únÚ©žnô±°?¸zG ‹ÂÓŒÏlc‰Q÷®#¿¼wMîÀó¾ à©ïÕþ"§Ar`,Ì «¿ó.óæQw÷»Šodt%Ü(Üå´¡ý.½„þ˜Wp;¹¹\0)Ú5fïJuãe€©th¤-ÿý¨3Q#²‡½ÝúP{ö{×·™·Ø'µálÉöªÍ@õkô/ô^!~•FH—òªtTà¹Àg’ÊZóç ^ži-àdÒ…r·ï(ü ð’úïœ4ü êö[4@źÍðš ð®JŽJ7ÿáÜ?Zà.òù¥€ …òX¯&N Ðߊ î þK¯ìh_÷qúÉ¥Ï6Q7êoyçÕbw]îÕ*ßyõ% S(‹²ÎµÐs› ¸<ðŒÎ+î¦÷”ÂR±³ÊØY¼Š7’¸÷ó;ÆqÁÀEåÝÓÀ Ÿßǘ°·|=‡Z¡œa¿FžËoy ïò¤³íëS2Ï<÷3Ö‘¸šxAV6ÀÿŠ¿ž8áÞ`‹ERõ8ÒPG½UŽS#^Zä&´{¡†?(ÙÀÛ-øÑ•®@†Ò¢°ò?Ý~Ê‘Ë Ài%Vhë?•̰“ãRVŽËº†ÐgÜüx#Î6J¶N˜ûv_T„ Cï`âŒéע…ÞT(·ŠIä­í¨ËÙÒ–¨_ÐúÕâ›—ï0Äå[E¡¢g䨾,’cý½}»Eù'{)mqêG:éÀ,L«ë0ØNª¨{ÅP<¶tþ¦úŒî¤çº|ÂW Ínû4r«g)ù ñï÷¦¨^yDsŒÙÃÑ{È~žBç]­çøáÂÓt7åj8,8èõ¦S:ÁCi¾pVHŸJB}ê}[|emPt˜$kurS²ÚUEÄ^yž'EéDI… ÉO8Š­/-ÿ6's;ôWJ&ÚÜ?í:¸j;ñ“¦ÉbÕ‰:ŸÁlcÉW&ÂZÇí¢Ûø[ݳ!xíнTÒçK˜'ƒ«f·©\,ްÔÉAIå1"~úÿHè|‰Õ±–%>qE³aAœ†àV8.ëæþè'a6?¾ý`ÕœGs]GGdtÖë³l5$FÒÄÝo7“cšHãba9— ;)dÁåÿ=ù{>1qaÈ"zTXtSoftwarexÚ+//×ËÌË.NN,HÕË/J6ØXSÊ\IEND®B`‚openxenmanager/images/usagebar_4.png0000644000175000017500000000066411636446664016300 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEn× ménØ!mèsssééé‚‚‚ÙÙÙãããqqq¾¾¾ËËËçççzzzvvvßßßÄÄÄ~~~ÜÜÜëëëB‚nà¯æ‡¯üv¯üun×!C~¯øyB†mßA‹ ¯ò}mèA–¯íA‘¯ê…mñ ®üv®üunßmò B~ÅÅÅ¿¿¿êêêpppÓÓÓÌÌÌÿÿÿÿ¯$2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïvIDATxÚ¬ÑI‚0Ñ8O€8Ï šÐ6ÞÿrÆ}/zAmÞ ß^Biöoc"#ç†Î ð|5¡³Hþù·P€[=’ä.²ô~îýD.vkíµYµ¡,U€ bÄÇZdÐ…fk :ÑN„yÊÌcègøO€áM;cÃ"IEND®B`‚openxenmanager/images/prop_logdest.png0000644000175000017500000000177011636446664016764 0ustar rrsrrs‰PNG  IHDRàw=ø¿IDATH‰í•=L[WÇï¾Çó‹1P"ÂA¾B•lÊPÄ€Ò*qBÓ%¡ÀÔŠ™µc§HQSu¬T‰*Ý:t®R¡»Šä%Â4NäNe È&?¿ûÑãâ8¢ê©Ct–÷Þ=¿û?ÿ{ß±Œ1¼Íoµú H̉Dbî_oÈ2Æ0==½ðôéÓÏ"‘Èh(Âó<\×%ÿ¹»»»›Ëå …€innžéîînìèè¥R‰“ð})%µï›tÃð›››?óIâº;99ù2N¥T5ËåÀA` …‚Éf³feuÕ¬¬®šl6k …‚ ‚À”ËAÍ¥”ùêÞ×&N›©©©ŸŒ18Úh 8Z…Àñ©ÂÀihà÷gϸ|ùCœ¬?³EFC&“9Þ}Åèh4J2™äÚµM‘Éd’øø8/^TXV0|i€»wï²¼¼ÌÄÄĽ€¶ ¯¯­5ªÒ¢66 ´æÓ¹ÛضÍÅ‹“OºVÀvggç—µ Ùlö R\¹z•¦¦ÉdŠñ÷ÇÉåru úkNQfiii¯`ÛÐÛÛ[£@JI<cãÁ÷h­™»} €žžž:ö©»wò¬ Ï Õè,ùe~yôˆ7n>æÉ“ßø`r/äÖz:ª—OKe„ GGGui­ioogmm ¥‰«WÐJQ*ùœ5¬‚ °¬P(äD"‘o÷ööÞkllÔmmm´´´ÐÚÚJSSžç™L&cår¹—@xwddÄíéé±òù<ìïïÏç­|>O¡P®ë~çûþ7Žïûª««ëó;wî¸Ñh4ì8Î9Û¶…aa‹°…å !\×uí8ªX,Ú2‚@Ê’R²X.Eß÷+8zõêð(“Ù~xøPBe¼fƒóZÚ•´ÌñïRP£´ÖRJ)•Ò2ùNK³®óâÿ¡ÿOñßùeK=IEND®B`‚openxenmanager/images/oracle.png0000644000175000017500000000273211636446664015527 0ustar rrsrrs‰PNG  IHDR(-SPLTE ,,-222333446FFGHHJLLNMMOTUW\]^cdfffhjkmnnqpqsqrssSwx{xy|}~€„ŠŠŠ‹ŽŠŒ“‘”‘•’–’“–’“—”–™•–š––™—˜œ˜™œšš›™šžššž››œšœŸœ ž¢  ¤¡¢¥¡¢¦¼£d³¢‡ÔŸ@¤¥¨¤¥©¦§ª¦§¬¦¨«¨©¬©ª®ª«¯«¬¯«¬°¬®²­®²®®±°®¬®®²®°³°²´°²µ²³·´µ·ï¬H´µ¹µ¶¹¶¶º¶·º··¹è­j¸¸¼¸º¼¹º½ºº½º»¼º»¾º¼¿¼½À¾¿ÂÁÂÅÁÂÆÂÂÅÂÃÆë½€ÅÆÈÆÆÈÆÈÊÇÈÊþÇNüÊPÊÌÎÍÎÏÍÎÐþÎQÎÐÒÏÐÒÐÑÓÒÓÕÓÔÖÔÔÕÕÕØÕÖØÖ֨רÙ×ØÚØÚÜÚÛÝÛÜÝÜÜÞÝÞàÞßàââäãäååæçææèæçéññññòòüüüýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ vpAg\Æ­ÃÇIDATÓcø Ê瀊@¸XXÁ"0Þh}+K#dÆÌ¼,k)až ">PÒT÷dÿP›Œpc wC[KAŒ¦š_|a¶—N ƒ- ôTu¤4”ÔU´$Ø€¶ˆé–…ˆ K*›zû:‚­-’uuŒ­u1³ äÇ) ÊEF$ÔØþ’ÛvøùtEXtcommentCreated with The GIMP9„9%%tEXtdate:create2010-04-11T00:54:10+02:00¤ÀÌÒ%tEXtdate:modify2010-04-11T00:54:10+02:00ÕtnIEND®B`‚openxenmanager/images/debian.png0000644000175000017500000000121411636446664015476 0ustar rrsrrs‰PNG  IHDRóÿabKGDùC» pHYsHHFÉk>þIDAT8ËuÓQ‹Veàgï¤ÍèDƒ…ŽJ7‰yuÒ¨D?Cð¯ˆôº‚QT*„S„À›&gJšŒ$LkùÎÙ]œÏf°Üð^¼/ïÞk¯½ÖŽ-±ì¸²Žö¼^¢â~ÇjÎÙs²™?”:¿l¾Ú% Cs°dOÔ'øªÔFª™éÕ‹©üEÝ)> ³ä4èš½™"o‡›Å×ÅF43á@± U4‘•’Et¡"ÚÉŸÅ4®Œd­—†š5 oq,ÝH‡Œ3( ¥‰ÈPe÷ú¡`ƒ¥‚¾UiPÓX/~Œ¦§4C%ý䌆{:ü=𵫘ê˜ÁÁA,VÊ^ç´5Èð(lP³%ߟ+ÍÏá¼þÀs5pÿFÛ>Ða±xn“w¢nÐüDÝ+¹^Ÿü[‰üZm­¦ß0W ›N\v µ§p¹Ôg‘~ÍuS«aèEì·°ÕýTJn‡/¨7ñukÚ[æ|>iòÿ£…‘nº4¼å½IÆÊ’ã]ôæž@þÏ2 TŽšøáÐd‘n–Z©Ôz*ömÙÂ'(<Ž1¶¯Ñ}KvQÛH«jýiþ'€ÑË"»"zTXtSoftwarexÚ+//×ËÌË.NN,HÕË/J6ØXSÊ\IEND®B`‚openxenmanager/images/usagebar_1.png0000644000175000017500000000065611636446664016276 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEãããzzzëëëÜÜÜ~~~‚‚‚çççËËËÄÄÄéééßßßqqqvvvsss¾¾¾ÙÙÙB~mò nß®üumémèn× nØ!mñ B†¯ò}¯ín×!¯ê…mè¯æ‡A‹ ¯üumßA–C~¯øyA‘nà¯üvB‚®üvÅÅÅ¿¿¿êêêpppÓÓÓÌÌÌÿÿÿs_JÛ2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïpIDATxÚ¬ÑY‚0Ñ"¨ 4 ƒ  Φûßœa }¨Ÿ»€Âo–0èÇKëóÒ˜µ1¾Üö™ë|§B)èóüTgmd-¤ Vw¥.‡äíZm¤àYVeul·×n!D#uÌ13§R0Ïð¿_]LЂ¡ûIEND®B`‚openxenmanager/images/suspend.png0000644000175000017500000000277011636446664015745 0ustar rrsrrs‰PNG  IHDRàw=ø¿IDATH‰–mŒ\UÇç¾ÌÞ™¹3;³Ý]ö½tÙnE[$–m¥Ú6~À@bHP 4ИðR‹ V]P?“úňC 4 6±FbÁhL0!&ÐF…j‰°­-»ív–íî̾ÌÌÎÌÎÌ{ïyü0Û­µmêIν99çüþ÷yîó<ç(ázM©]7›¸Ÿn‰Çz0”á•«Ùå‰ýðº{¯% Ô=©TçºÑ-_Ú¾gçŽk‡‡»H¥(Åb…ññ,>66sâï¼RÌÿDä÷sW‰ÈÝqyôË£oÕÿ0&2^ÉTD&‹"góÍ>Y™ªˆL”D^?)²ëÉãÚ‰îÝ5ÖÜpÓS/=þÌè£_Ü–FjP¯JÁÅ•jõN ˜-ð§EžûîOfÇÜMußôÔ¡ý‡~¸ï–~ƒrDÀ0š`%M¨Zuá%A¸.ŒMiž}üÀk³ãî½BÀïÝóð‹OÙ¸.E©Ð£5…\¥4ÊpHuô‚‚ü|–zeeØ´¶÷s]â.|x¶Äáo~´RzáY ÀV_‰î|ì/uu¤˜ø(XùR…W™f÷H%\Î^¨ðÎdH¨éEF¶­A”Í+oæ¨Ôã @æ…Îö$[¸ï[}õ×¾ü6g´uîúìçÜ©,ºiÍ(OpÛ'ol:Û¢¼þÏ< ?`÷H”áµm؇®,26;‡ÖŒEƒu·nb¬wý“ÀÀÚÛ...³x.ƒi˜¦‰¡ ¿B (MÍ ˜Ÿ[ áûxžCjl T,17ç£EÐZ£ƒ€X{šž­#OXqõ`ÿæokèÜé –çç°#öª@§UD5ƒÑš\.‡ïˆî]ýÛ•J‰lv©i~` DnèJ»ÆC›¬H2¹1°¢d/d–‹X¶…iš˜†I2^e ÑšB±@„ ]«‘W«–)|DkÂPê-BÛÚA"k’7[vk¢»VÎRœ~eµ‰¥1MÁ4C|§ÁÅ`­©V«èP_–7Aã^óÐBÂ0Ä…x ±â±~KÙ–„å9Ê㿉 Z×cÆû°ˆ(”DS«ÖÐZš ²â#¿áQ©”  ^&¨-T³$’uͱ‚ ˜µœ–ÛMP˜AJç –ç —Fª{$hf§N#¢¶¬&\uišüùSÐ(AP„FtƒÏ¡E>6<¯þé$ˆ­ÙvZÒÍ·¹ä ðÊP[Iñ•¦ýøU” ¦‹ÑºŽhz€F£zÒ¨dŸ› êËgÚ†¶‚# VìbÄ›îYác¹`Å/" {e}²9oFií»°Pž:xÒÈž{¹sÃvÛz FŒ8†íbÙ¶mcÛfn%ˆÅ¢Í1€Ñ¦ F ”N;Ÿú§¦ª`ÆšF…k6l¦Åm•Âô_Ÿ¾¬Øµmÿñ½›îºïè©7³pú=°â˜ŽÃP¯C‹e’¯+ ”"îhz’àù >šñðë4*Ä»zþÂL½{lßÂñï¼x™@ß®Ão»kôßo`aâÐ!–ÓB*•&N‹Å0 ÏóXZZ"ŸÏS«VA„Dg?CÛog>óþ‘>øµ+ÊõªÈ¿<8ø™;GscSÌŒ½K,¢èé½7™À4Ljµùü"Ù\–|¡Dr`˜®[†˜™øû/¦µû‘ÿæ]õLNßóüýC#;_HØi·V¥ÕÚ’.ÉÄE*ùR™Bݧ`Ù,zùZæäßö^Ûûóÿe]ûпãGÑŽÁúÄׇnîO¥èI)LÙ%È䋌OeÎgÎyy~ròyýÆ÷ŠWåü_×–=‡¬„»!uzP†jÔê3A¹|FŽìË\oï/шˆ…ëâIEND®B`‚openxenmanager/images/add_server.png0000644000175000017500000000272111636446664016376 0ustar rrsrrs‰PNG  IHDRàw=ø˜IDATH‰}”]ˆ]WÇksÏýš'u2Ói&¡MbÍ$jc¤ …© R¤…" ‘Ö>TŠ âGóPEA ¶ ñAê‹HPQüˆ¶XÄJQÚ`j­ZZ'±éÄI&N&3wæÞ¹çÞ{ö^ˇ;™dÒê>löbíÿ>ÿµÖÞÿ%üñáÏÔùÀà lJnO>Z+賓Û¢1óÿîKIS}æì«Ý•ß~§Áôñâ-ÿ#×;v¿?ãÑ_Þˆá½]¦¾]-Ý4½Ëjo–nX&‘•t”j¶:/>gչϞ?Õ>søî‹ôÚÿ‡àcpàë£4¶«ïß{vù9šÝ3¦’*$˜9(DswÝ2ô!³­Ì®>ûÕÒ`ñÄ¡wŸaiÎßL0õ@•O=9>Vmï;½7p¡ù¼%ZE%ppw@DÇݬÀA&G>)ZÏÿLë??9Ãê¢_%xÛ˜ðÝéí’µöÌ·‹Ë#Ëù)K’Š`êÚ‰MèÓX¢™–¨Šö=f…çzëÈý\lŸøÖJ£ñåÇöÍ|ñ×ã òÛ/¯¾†hÅÝDÜñNX‘û·ýŠ{·ejô+¾Ô=#sù+.^Zè+%Yh½ÂÄЧ¬záw«­ÞÜ™—{èÈÍ ;öU'«l=p±ù7S©.b †‹D‹6Q»Óp ­µ)‹6`@M¥j³K'ÈÂ-?¹çK›Ð;>^Ç:7|m1Ÿ/«EQ‹âk«ZQ4R(‚=¨ÙFŒEQ·Dó¢i}wy(}ï–=%tçH¬hv.9$B‚ÛúŒnžž¹Ì7` Áœ”ÕÞÖ+?¸c…td[vs»ÓNÝS7sœnhÉP¶Å¤ÿxģɂ¤”­"7H‰º B —¼X±,©‰4óy6e[ï½%#ÕD¶w‹nâ&¢ØäàÎÙžM÷è•€]ÞÌöŽÔ½#¯õÛ±Óètã÷”´bî‰tz­åZІ¨õnèM%V³‘ò­‚cffî.v͹ÞÈæÊ¤…h ‰¦Þ)òJtHÝeÙ‚!’8Žº§´CÃTELD6d ªzßšÅe5KqI ÐiâBÚnùÉJ=!š£@&C}õìžÂÖ.·›<¼û§TÓ!T•.þÎÿ€’V„à^_9AUëë×MÐé•K‘ôük½ùíwÔŽëEWN^þ3Þ—.íÞÑ®vËFç?L/¼@9XkB&ƒDëwž$eþ4÷¯úƉUHôÇÑ!:sW2Sʦ”2Ãu]hxjBYÖöMÈ$ºØÚy‰.h9ùáëÇ[èKO/Rª&‡˜C¢kÓ7Ø1Q]š ®Ç\µÅ?ÎM÷f[—# xé饅$ÓÇC?‹7Mó„¥îÅõ]Êgq’·Äʃ¥‡ž}âüÆv}èøm„ŽÿÕ}k’ºÒÔÅ1Öe|ð+äbë /'udCÿ€d58ûbëçOšÙH04^â‘ß쑼ÿú¹ºe€ºæSMU® ¯}iê_È›áÉ£÷M¯g›\1º-cñ “w~¿бžwo7Ü7Ä]Àpu7Ä 7w ÄvêÙGâbíØSŸ>Gžçë²k×.Ž9‚»KÑi‘ÇJGVzôÜ]³å—¿9_:µ_I\®ÆÒW‚¤Vkmë¾ï£½]ß°±åLª’Õ´“¤IHÓÔ>L:>>NQ;[­ÖçTuå¦Ä²ÊæðΡ‹wÅH±°Ìlj%óIOr„„ª2&zö¥eÄ{#Å}¨/äy·eTõd¥RùÞÄÄÄéôܹst»ÝÓ"òPꎕ\ÝÍ+#¶½6j;Å­/ÕèBóÐ63Qtè«@'„Й™™á¿Í»vÿ¦ö…IEND®B`‚openxenmanager/images/prop_customfields.gif0000644000175000017500000000235311636446664020003 0ustar rrsrrs‰PNG  IHDRàw=øsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÙ'­ÞLþkIDATHǵ–]lSeÇg]ëèZºu“ ǘ’µE qpјhŒšHÌŒ› `$o4[/4!½p†,à ‰ Ü š¹Y([ÜGǶî³K×uß=§íyß׋•¥LˆbðMÞœœ“óþÏóäùxá^€ßï·þÍÖÖÖáǦøý~Ÿú‡ …Tggg²®®®öq´s2Ïb]O LËÛ4MR©z"i¦±Ú¬¶ .´îÝ»w÷#ôr¯½Ïñ¡ov¶†Nïø°ædƒ”TH%R®l¥ B2™Äív[/^ºÔ¶oÿ¾Õ[Û´Ô}Üø•-W{yüÖíZÀž›ý‡)LFÇ1 —ËEII ‹…d2ÉÌì ‘H„3gÎàp8¬G ºñE{{û×€v³½¦±‘‰`H0Èm›; ßú@HɺҒ•÷d*I,cddx½^***PJ‡9|øðI]× švßþ,[üÊõ­¡Óý]Í@r „ @lj ,,,°”H09aË–çñz½ i En7[|>›§å½ăÁ>ï ~ Œz@bè¹V+(È[“ǽ|>‡w»Ÿ ¢|=©tšÁáQ|?¿Îjñ‚Á·>` 0³‚´™&>G˜&±éyyyx½^wÿd~a‰Úš˜R1¿¸„¹pçaâŸ]ëSÙiŠ).×Z\.fgçØTY‰’Šá±›*7bÈæ “[m¿R__Ï÷/®ˆ<8ÄÀX])JŸž!"™`}YRIÖØíÌ&ñDš®Á±Ì‰ï¨ßÖÁÅ’ãØãÀ%@ú¾å+…‘"aš8œùØRV@[® )™_JŠ-2kZOÌ2ðÓEÂÏbªµÜ˜‰¾ùÖÖuEEÅjµÑz©ÞéxœØT Mƒþ{ý(©˜Ÿ_âú^"Ós¤…ÄV½QÈ ¾–"·[{¦¢‚áááQ@T«@ ‰§ÓI»u¥¥„úBH%ÙV½ «¹Hb.N¾ÃAYÕfŠË6Rþt)ï¾ñªÝf{Š–ß[zI@>4Dfƃ…ùy #IËE pƒŽŽöÔîA¡q³7ÌPx a <•åìªÞÎÖÍ•ùçÏãìÙ³`ü‘!hPPP€ax<Õ““CÍÎ]¼P]µÜßP€ÆùÎsêÔ)z{{¿žŽâo%$Bˆ•1a±X(/ß@"¡sõê/ôõõáóú¨ªªƒƒCôôôpâä‰;Ñhtû¡C‡hnnþ0C>’ɨ€žïÈGHR M[öÓétàt:(ÛPÆðP˜ËW.³¸¸€ÝnÇápF‹€ßš››_ihh ©©©!£yHߟh/Åãñêÿ:»»» ×€º  )yìé§ùý~-ë“Ø¼\TCCƒÊ„ªðIÍv;Pì¿.ëµ'x°%™L*ÎÔDö„o)VÀäeÞâ_'\dÙ¹ð÷IEND®B`‚openxenmanager/images/info.gif0000644000175000017500000000176411636446664015202 0ustar rrsrrsGIF89a÷þÿÿúýþóýÿ÷üÿùûþøùúôùýñùÿ÷ö÷ô÷ùîøÿéúÿñõøìöÿêõþòôõìôûçôÿñòòéóÿñðîàôÿåñÿêðöðïðéñùëîòíííáðÿÜðþêìíåíöÝîÿÓñÿáìûæëðéëíÙíÿÕíÿÝêöØëÿâèîàéòÙêøÕêÿæçéÑçÿÑèÿËéÿÓæùáäçÜäíÍæÿÕäòßâãÖâîÐãøÊäÿÉãÿÎâõÊâù¿äÿÅâÿÓßìÚÝäÅàûÁàÿ¾áÿÖÜãÇßøËÞñ½ÞÿÍÛêÇÛñÁÜøºÝÿ¾Üú¸ÛÿÑ×Ý¼ÚøÌÕâÀ×ïÎÔÜ¸Øø³ØûÀÒä±ÕùÇÏÜ®Ôü´Òð¸Ðé®ÏòµÍä¾ÊÖ³ÉÝ»ÆÑ¶ÆÚ©Èæ¤Éî·ÄϧÇè¢Æê£Åæ«Ãâ§ÂݲÁЛÃ뱼ϥ»Ô›½àž¹Ô°µÃ–ºÝ¦¶É›µÏ’¶Û¯¯Ãš´Ó“³Óˆ´áŒ³Ú™¯Ê›®Á¯Ðƒ¯Û”«ÃªÍŽªÇ™§¼‰ªÉ„©ÍŠ§Äˆ¢É‚£Ä‡¡»|¡ÐˆŸ·œÄ‚¶rŸË~¾’˜²‰™»~›¸{šº{™Ä‰–´|™¶w™º{—¶j˜çx–¸ƒ’³s–ºl“»p‘À}ޱ€‹®‹®cÂhˆ±n†­V‚Ï_€²ƒw”e{£L{Ñqu›_n™pfˆmb…Pd•S`Œ?c©/aÅ5Z¢JU†_Qv1X #X».V¡7P€1PŽQAi?At2CzM=fE™Eª48m:‡@.Z/.b4œ3š.•4"P*‘3!P)ˆ*…&†$]"~$Mw @S9ÿÿÿ!ùÑ,Ñ£ ¼@‚BŒ¸ °a4zá(m\`€-W­ð‰@")ˆlp ‰‚ 0ÔèÍ „0Á”BUʈŸªD»Á‡£­‚½êÀ¢) /·ºüQ¢D"a²šÒØêEWšM_¼ˆ„•F޳JØèÄÅ…‹³diåB÷L!bˆôhñÁwÓ0_0–,!ó‡™@G}ÐLÙbJ”%QTÚ Âå°Ñ£A{àˆÁgP&êbUªP̈…^íG ¬Õ°£-ʶêÐ;openxenmanager/images/patch.png0000644000175000017500000000340711636446664015361 0ustar rrsrrs‰PNG  IHDR szzôsBIT|dˆ¾IDATX…µ—Ën[׆¿}9^ÄH"E_¤(妓¤já÷è;ø2 `8'™µàYE¥EAŠÚ.I›¸µ|‘h™¢DŠÉsö­ƒCÑE9´ ØÏÆ>ûÿ÷¿þµÎ¦à|È9sÿËðÓbæwýã?þ¹sîÿBBkí?ÿüó­µ Ìˆ€Í/vþ`¬Q§“R\„x½TŒç}!ÎŽbñ`1FÓJ±±±ñ›^¯÷0zf]ÅQ3ÔÑÑÑ™MJ¥Ãá!ÖZª• Æt¼GGåR ©Æ¢(B+u†ŒR*éôÁ53!„œœÆZ‹R 9~!¼GHYh8yO`LNÖÚ¦”¿b kP[£²´‚Öⵊgä™C´Ö,//Ÿ‘?„@ma¡x–’ÂdS!Y–q²÷˜wìKtòÞ÷ôG=ªÍk$iŠ|­Æ›(Àúý>Î9”RDZs||L©TBJ‰ó5–:Ž"Œ1˜Î6—¢:-#uŒŠDçÈl—þ^@­þ­ÏÁ/91#7rÞãœ#ÏsÔXS5ô¨MSwQÓ9 uB””¨ä/鼜¼3Þòb”R,--MˆH)Y\\DJIð¾H˜<§¿óOjù*Š‘R‚à= J#uL¬ 'ÝçäõËç¸0½^ï=JJ„,ŒY)—iïïSJÓBöç\M‡ÄËï\†#¼#ˆÓž–%hÛg4L`.LÁ) )%RJ”ÖH!ŠJ96œë>ãJ2 Y\ŽÂNéB!óxoñ6'Ï2L Ço¡œIÁôBмԤ¿û/âÒåuòƒ'¸Ñ B¼ÍPÉÁÀ¬ÉÙ”¨ÿìCjoçïÝÃCrcHâ?Î{¹\æåX¯9’¥5²ýãç ‚Á“eC|€´TÆZË‹A™•_±²²2)ë7*€h­i¬¬LZ«÷c ƒ½ ðÅ«d'¸~—ŒÊïÁsÒÝG¬¬²;Z`ec“F£ûhÖsSàœ£×ëá¼§”¦àºÏX­ä$KkäÛØþ+üèoFˆ(!pÞÓÛ{A¹~•Ç’Åõ÷BÐív©ÕjDQtî¼ç5„ Á{ŒÉ ý]V+y‘óÃçØA—`F›ÆÔ%’në)ÕÆ*mq‰¥õØØ¸Ž1Æ*NÅ<0¥µ–¼ó.«Qù*ùá3Üðˆà !8„N:!M¯Ý¢Ú\祸Ìò{Q©TÐZ“¦)qŸùšNÇ\H)Iâyò’Š<@ Á°ó o q#¬DÆe@àí¿¢ÒXc_]áÊõg ×l6çá¾ÙÆòÎSªÙ ” Š4'ƒ#œËIKepEuôöžQ©¯r­±rms>iã¯Ûï\ÎyÀÃqëGªù.*J@E BH*µE¼5ŒNNº­§,¬¬ÑÑWi\Û¤Ù¼T”ñø»a­=3œs§0{àø¨‡:~JBiPÁæ@(Iu±Î wÀÑ~‹…Kï²=ZdåÚ¤i‰íímÊå2Î9´ÖÄqL§Ó!MSÒ4¥Z­žŸK@Ç ‡.¦‚1Hªq{µ¡¨¼S'®.Ñ2ïðþ/oÐh4¨T*Xk' xï©×ëxïB0MÁ¾Qr¹L»Ôd”?#•¦pºÔÅ.‚ÁyAË,Ò瘀:犕1Xk1Æà½Ç:gÿüÅ÷[€»€ÖšåKïòô»]®« ** $6À㮢¶ú!Àh4"I’IÎ1t:Úí6qS­Vƒ÷ž¯¾úêOwîÜù÷~‡©«ùÜ2\ZZÂ~pƒ'Ûß²–ôIãXcx>ª±þá¯i6›ç çœ#Ë2z½›››„ÂîînxôèÑ_nß¾ýY«ÕÚ²7z@>¢("]^çû'4âµØÓ:QÔÖŠöÚét&’$ÁZ‹”’<Ï !àœ£Ýn³µµõðÞ½{ŸíììlƒY¼ó 0¾ù†Àòò2Õj•<Ï:Ëµë –ëõâê5^wšóâVlȲŒR©D»Ý[[[ÿ¸{÷î­o¾ùæïÀÑ<¬¹”RÔëõI㘾šMÏM7(Œ˜ç&!xøðá_?ýôÓ»_ýõß.¿³ÿvfˆœž~Ng B`ùäÞîîî·ÀñEà³`‡Ãá! .ŸU`ºãeYæ¿üòËßߺuë·{{{?0'ççÎ:õ[«7oÞÜôÞ«Yyr£‚¹ÿþwƘ3n§$æ§åí"P4ÿS Oã¿3)s-˘øIEND®B`‚openxenmanager/images/confidentiality4.png0000644000175000017500000000141611636446664017531 0ustar rrsrrs‰PNG  IHDRóÿatEXtSoftwareAdobe ImageReadyqÉe<°IDATxÚŒS]HSa~¾óm:e&1M¥\”H 椡K±‹’~@´nŠn"Ù¥]xãꪛˆAAôK–]Ò´²ìGRšÍ ýl£¢é2Å&ÁþNï{6syÕùx8ç|ßû<ïÏ÷¾¢¿ï,Ö=m„ý„FBUv/@' †sÕœoÁUº©¬«º¦ÆZY¹Õhµ–èÑè|C8¬õû|s‘å!„øL:›«dwSK뱃‡ŽZ­%2°2ÏÛ‹ zQ¶±»wÖɺÚ=Õ V‡ƒŸ+È~а´‹Èí‡å^ànL] DJBI+˜½‡BëDý)t8:,dßþÔûxŽÞ=,ÐÆa7Ø÷ Îàæä5( "©éŒ€ ²¤u÷õ¤¦ˆÃö#E³~¥3¬pÁ8縈ãÖäu¬$ )dJ%‚ U£o*cpâ>´˜Ã\Ž ‘ ö.2©E*ã9•La1ö¿~ǰ°²EtT\PŒ‰ðl¯Üfd. Tqµ=c— Ó*’É4ßü˜[šG"‘ÐIÂ@ KAÆ?Iô¼×‡'qíªþ^£Ô$"‹Ì|™E<ž€–"‚Á¡VQòÖîžk {F“Í ß×iÄÉ«–†îY'›2™Ÿ…è¶»¸7ôcqj’xýæÝ+ˆ -2{VŒ9"fCD³m¡`œ¹,0D5“tÿ74-¦ž³š EÔ<‰Â ¸/ÌðOû¢ÌåNü´‹•KUVw:›LªI<ù0ª“„q- ³¥}ös8½£[{õâù2 Ü&Ë«Eô<ñ–r‡õ¶ö¹œ.Œ~Á• ú™zš·´ ùx96¶L]ø ;9Ó¸6L»h˜lÿ ¸NœêúaÊFÞèáö$ü÷8ÿ`'°Ï xIEND®B`‚openxenmanager/images/storage_detached_16.png0000644000175000017500000000116011636446664020047 0ustar rrsrrs‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs»»:ìãâtIMEÚ!3BðIDAT8Ë“±n1†½{¾Ktº")ˆŠHÍU¼FZJ%< % 4y ҤХK“»½x×öx†Öì #Y–­™Ï¿ªb¥àÍ0 Ì|ι;ïý€ïÖÚÃU…ª¢ëºóívû%ÆØåœ5¥TGÎYsÎcì¶Ûíç®ëÎÇ:£ªxxx¸<99ù䜛€ˆ€ˆ`Œ98d\—Rú§§§wggg×FUñøøx?›Í^L•c@DǪcŒ÷«Õê¥f~æœÃ1`Zx aæç0\ŒÖZüOˆ†ap1FÀr¹D)¥ÞwD„ý~fþ»7y>03ú¾¯JRJˆ1b8çª/ÓëTÎ9Xk‘s†µ"RUûýD"‚µcÿ¸? c4D„ÅbhÛ)¥zRÛ¶ "„PJIcŒ÷¾Ê>===©ª`æÚÎ9¨*U€ˆð0 MÓ4X,`æƒ&2ÆTDd4¼LMÌóùÖZÄὯFNAÌŒRÊKBøÐ÷½”Rж-BH)ADPJ©³µv4ZBW~ÿ¸½½}Û¶íGïýëÕje™ùE¤ìv»!„÷ëõúë`Œ›››™÷þ²iš1敪BD~ŠÈ·¾ï¯7›MœæÿWXVTo?ê0IEND®B`‚openxenmanager/images/tree_disabled_16.png0000644000175000017500000000073011636446664017352 0ustar rrsrrs‰PNG  IHDR(-SsRGB®Îé·PLTE%²ÿÿÿ]±¼ÇÝäë°»ÆÞåëÍÕÞÎÖÞËÓÜŽ›Š–F¼ÅÐÐØà0;Fßæíÿÿ,7B£ ¶ÁÌ{†“äêññ÷üz„áèîw‚åìñ^fpNÁ'ßçíêñöÐÎåÍÎânèC®¸ÃÎqØ9eny™ÿfny„ƒœÔÛämvq{†ckur}‰ÒÙâÃÌ×hé: ­ƒœit~ÁÄÙbkuJT^ÉÑÛÅÆÛÕÝä/:Edmw†eútRNS@æØfbKGDˆH pHYs  šœtIMEÚ ½9-dIDATÓEÌÇÂ0EѱC7l¥'J@Bˆ½üÿw1Å wVï,À\FÜÆgT›ˆiòz†Õy#—=_3dIÇ—d EÔõEÃñÿT1ìßIiͰÞö%¥-Ëî4”X®Ë@" Yµ/ P¾ç v³–a p¼—n8<Ó*­ÂÅ6<>SÌ9‡@û"® ­ibÝIEND®B`‚openxenmanager/images/tree_starting_16.png0000644000175000017500000000142011636446664017433 0ustar rrsrrs‰PNG  IHDRóÿasBIT|dˆÇIDAT8}“MhTg†ŸsçN’‰3©™ªã_ýÅè4Æ&¢F„–ÒEE\AÄ¿Ø`D RüYêF)âB°Ý(ÁDñ§m@!)èNc´ÁG…V'16ÑëÌd2sçÞû}.nŒF¢¾ÍÇá9ç}_N€ÏT[[›Y__°m[%‰ {äã®®Þðåáºb»“™}V!žôJ¦ Ø¡y7³6ç*+ãO? èéí]6•çMáÇ¿W’èlíJPå ÉUn³ÒsÖ­¨¨8èq€Î®®¥snDî‰1œF»‚rĸ‚r ðÀ^± «zçá%_W0Z[[CÓÍ¡ÆÈýc1ɦ@Cügòűqò´‚í ÷\Ýßq÷Þ·c€Y³g¯ (SM(ày%¼ |ßVàôa‡]TVj.á¡æiä °ÕÝÂÛ«gœD8ÿÁœ®@!¹`Ê­y2äÛ?®—˜`œôÿ!LyIJd)YÆ %2-$W˜q`zA£Hñí iüÔ­üãÄ ø]·€vˆûk7®æIÙ[·oCÅ™wBçôÚaؘ-ŽNÿõÚæ×'þy‹ºA¿¢×ÏùK€V£Á}a›1IEND®B`‚openxenmanager/images/tree_stopped_16.png0000644000175000017500000000132511636446664017262 0ustar rrsrrs‰PNG  IHDRóÿaœIDAT8¥RMKQ=ïM¾gR™ŒD‰ºp¥mqg‘nýX*DWÙ‰¸j»܉®´]g!EDôøŠ"H Y¨%M-ÕIL3If2™yïuXÛU/\¸¸çÞsï!BüO¸þfææÈÏÝÝà¼@i¦=‘øÔŸJ=™FþÜàc08©*Ê»Ððp¯ÒÝ PŠj.‡ÂÑÑ×¢®¿yU.ïý“à@Q^wôõ­‡“IBdù±0 ä77ÅÕÉÉÛ—•Êû{œÞûÁàh[,¶¦ÎÎx½`…¸`œ£žÏCx_Ô¥(KKpG"ëúX]…¨T­Õ~·ûùàííõ#¥[ZLÆöü„ÄZxîp €Àâ›_’&J¥ô#¥ÓiìÎÏŸü’åAXùÎùùçâ‚sqÅù¹ ¬dyðC2™988x°òââ"ÙÞÞö–J%¿mÛι_áBxU €€E)­QJ —Ëe*ŠbŒÕˆ¹\ëëëôøø˜jšF ÃÇ¡Œ1”Ráv»¹Ïçã­­­,ó……ÇñqÃFû„¡ÑVIEND®B`‚openxenmanager/images/server_alert.png0000644000175000017500000000260211636446664016753 0ustar rrsrrs‰PNG  IHDR szzôtEXtSoftwareAdobe ImageReadyqÉe<$IDATxÚ¬W}L•Uÿ½ï{–%†È‡óƒ/L>…Õ°²f®þp³-šm¥kµrkÍik•úOµþ±"ˆÐ´Ðp µ¢„ C³¶V©-æBùºï{ÞÓóœ{Á+\á½ÀÙž÷ÜsîyÎùßyžçLM ž_ÀØûÎǵ‘QK*’ã m‰É °†¦k¸t¹ÿu¥þµ]Û¡.1]®WÞþ¨Þ†^¾2e‚‚\°0 pÓ´ðSÏïÐaŸ¬zöñ §L¨µùã1ïåw«¥ Æ [ÈaSÞt <ŽÇ³ëó<0àòi‡ØBÀmÚ8ÛõlÞÇ€«ãÁú<É §º¾4›>¶m«Æ¦5qŽ4uõ*=û–i`:˜Ú¹A†u ¦:Õ“Í&`‰Ìäh¥mþð™xjqCÐ& Ç~±žœ)Þ%mòki ‘Ž'áñ¬7; X¯¸U‚—ä)Mç/éÍ Ÿ)턃ËþÚåãSÚ-Êl±^àËûaÀ*ùüuMŸ€æ¥žõfÎé ¶2¨Uq‹Ûg½i¬?‘aÙÊ¢ 3’¢©îŽB§;.)½YñÞ ŸýÁÚ6ª§+’mÄÖ"¥7;^ÀgªÚ€6%MªÒ³g €³ˆJ6¨ÑaŽ'áñ¬'4×ló ”d­€Ó„¤á»‹ ;Œ ј7´-ŠÀ›uíÐ\+ŒqeÌÝ\P4T s:__K¢£éÂð=Ÿüܘ<¡ëê>¸ÝêoY¥ôþ¥.!Bj fhÙ“X›Y ›úÖ :Gÿ®&1 DlÑÉKçßfLµNà =DµM]@¸ ex–{#¶†X³‹#—f‡e”r®„ºRjžI-Ûz¨ûøâº>Á¼M]0¾bñuTµÆ£ª-ž¿`äÇ#¯d9,}ÜÃ(ªC|Ñ6 ¿“äG$mEL˜QO3;ô¢’Îò6—’£¬ E÷î¬‹Š Ó}§O\‚2êê|1pEEà¡ÊF¥Zw¸‘k qæíPÁþΧü%¬º?à#(ÍM§dÓDñ}锦Q“Ž÷6e'cpø?­OèÇæ¬}¿ ôÏfDm|øõá•Jøwtn „…ïxk{R¢¿°6áLËR@Žùeùëq¬± åÞº,ê¿nÃæüLj·S꛺Ñ}ËÊiƒ×ˆmчS(ø¸Z‡å¥O -fn ­ä€ʆhEá|Ùô-¶çL¨OR]Q”ÍțӃÈðy]Dyhÿä„¡èï7•ðoôw`þÂëˆHJ¿·a÷šçƯéû.ˆ¨|õȵҒ\¸ÝæXrzÇÌGXñ­O#sÇNÚý‡ž ©üÚ3hX÷: }–ìÄ÷¼¬½-s©cÐ_Z.‡o´uê\ödPªÅ Ë`Y ˆËˡ龠Eçy6ç²ñØÁ|5¶úù³P BëÀçˆÙƒæ«7¾Ñ±eôñâËÏC>!Dû)I÷„„ÕîJ­MÝMÊ‚mïÅ¥sZíe@èž~éí'ï:ô*Ò^jå·Ãx†Hþ ùÇAn¯?™Uà.¢õŠJÍÇB£&Ç”¥oÈä~¾a Õ}¼h~^ÇN ß: IbIB¼®“ôríïuìø5æÝÅ]NŽk\a³ qóÚÿ 0H¤íqMûÁLIEND®B`‚openxenmanager/images/tree_running_16.png0000644000175000017500000000140311636446664017261 0ustar rrsrrs‰PNG  IHDRóÿaÊIDAT8¥ÒKhSAàæÞ$7I“›¦QûL­EA”va%‚Åç¦.ºÑ…A ŠêÎǺŠÝ(H ê²Pˆ(*T‹‚µ ¥V«©i‰}åæ1÷•9.Š…Z]y–ç›ÎFDøŸRÿvxgò8{“¿·A’\œñ¡DÙþw+¯/¹ý™àØPd¯ó_®£–Õ:ÅÁ‡ÁSø†·#Z}ñôõs½ÿ¿Ÿ N®íÞ?Ä4OplÚ<ýv‹ŠÕÃgno6®,Žôë[•Tͳ=G¹7"á 4o ]À)r3~¾)ŸÜ~kSöðßR~B^j‰ïâ¡zn(ƒ åí(I6 HOJ$½ÞFbånžŸ—~÷q8ÐUxk¶Ô6†AJ®¨Pë±3zuÊFÛ@ H¥ˆºF¾š-ú¢±ÀNCõ²*Î=ÄŠ$æßÇÖ…wbGùYhvfœQ’Gíò*n §apMXŠÇ¸¸‘¹h€ežr$"æèT¯ ׄµ°éÙlʰQV˜.Í-†¦žàƒs Þhð3;nh¤/$HvŒ[c_gïæÌ)0n€Á˜.¤Ñ7~Ÿ}W¡ë%ÕJáËèôÝdǸµèÌi~áñ«é‚eÂÏ}x—~€~ó$+FÑ‚(óøa:6½ü˜.N± ]¤mƒMåkXosKe|U´_Œ†UÀ×Ù ¼øñ}f˜ö>?_\ "™L²—ù›O•8¥¯dû|a¶,ƒF²ctßNû¯$¼‡²ííí²µµuèêêb===¾l6ëw' ¥ô‘FDšêG\À✠ιPUU„B¡b[[›Éˆ©T ÝÝÝ|``€g2.„P\×å¥R‰€¢(¤ªªÔ4MÆb±Rss³ììì”MMMø¶ËHžOÕ¾õIEND®B`‚openxenmanager/images/template_16.png0000644000175000017500000000116411636446664016401 0ustar rrsrrs‰PNG  IHDRóÿa pHYs  šœtIMEÖ&(Cj ÷IDAT8Ë•’ÏKTQÇ?gœ‚Bƒh¤‹h”­DËEà¾fz „„ÿ€; -d–Ôªf´³…›à1‰ËÀ #ZH#1â8þ({E“6eïÞÛbÞË÷& úÂáÀáÞs>ß{®Œ-f³ÙaÇq°m›pÎçóßfff–'''Ëãããå‡7>Ü%ªçÌÎΚè£1æ‰1æ–1f`¡ÐmÜÕÆ­¶b¡ÐmâŽã0::Šëºh­‘`‚ x~¦öÌÁ°Â¹‘ÛÄmÛ@kM¥RùsÙÏÊ-" à̈͗jcL«A@Ð××w H–æas±D×IÃ~c;J "_: Ò—³|~_Äø"™L¦Ý‚†ʘN‹ÅÔÒUŠ­*s'â{è¼b*ßInpþtJ÷§Sª?R~#6_=mM÷=DÖÊk` ÷?.ÿý l?,¹ýº8Ì‘!ÌÈájûÅ Pja`øÊùåÉYö'ŸÞ|¾ôøá®ó³6¼Ø ,]¾ƒôØ‚ÿÿþ:üýÇÌ$$JzÀðÿön0#L(VÙØ Î??³¾yþ’õÆë÷ÿέÜqåÊË·ßoï;ÿçÞów`C€œ ¶àë÷œ¼œÀðçz™YLŸ¹d0°BýÉðùÅs¦‡_¾³\Ø~êï¥ó·?\»ÿôÿ­Ãÿ½ªþ -r€i‰j(3ƒB$@Œ :YM‚Q.Þ–i¢©»•Ž6ã¯ß?~Ü8ûß™'n|øÊp}÷i†Çï>ƒ]öêv¨!à jØ/¨<ƒŠÒÿ ³l### aaf`ÆëO`Êýú’a†ÁŠLX6ú Å¿¡ü…Z/šAfÌF¨!ÈÞdB*u‘«‘X0RÞEÒ4 €X ø ¥±îè`50Zô÷¸‡Õ½êIEND®B`‚openxenmanager/images/server2.jpg0000644000175000017500000000757111636446664015654 0ustar rrsrrsÿØÿàJFIFddÿìDuckyQÿîAdobedÀÿÛ„       ÿÀZPÿÄ£ !1"A2#aR%q‘±ÁBrCs$´Äu…v7!1AQ"Raq2B‘¡ÑÂ3±Ááñbr’#sÿÚ ?÷óDM4DÑDM4DÑDM4DÑDMqÞÕíSuÁèâE£q&æ4éŽtÉÅ‚ bÈIŽsÜéŒÙ8"lŽù·Ù±¼ooåÕ©ñ-ƒ „º•[yÞFÜ#Ê%©ÍK`Ã!.¥óê~Ù/eÈÈ!É¢M"Ž<hès‹8%âËQ\xÜ×µÐߺpTÙ[óoº'Í›yˆ‘Ó¥³|\f#Ò±Ùw¡¹ 4tél âã1•ÙµhV´ÑDM4EMû gg·1®še¶N:ÀXÛÖŠËÛ€8Œk>,cž'+UÂvÍB""½Îõ]ÑnÝ‘"Ìj@l‹{ˆUÞà=ÃAÈ-czô¿ÚCÄWú˜˜[ü³]¯D]eþGîQ <Ô ùˆ"a“*LoµY€“4¬‘Rá ¡š(\ÔxÃ=Êç:S}8"l‹ó|i_%ùL6ˆC’R3|n#(šóµW7MÈvIŒH.plp—WÌóîÔ‘’Rá5“"Šl/5/¼:TÂM!ŸZªw¶D(¨4Ú ª§7ª«¿Fëù{å?)·¹Ö1<ÔŸQ¦8i\¿ä?%‡}jŒ’ï§ŒbÞŸVTþðÁêiÒ–šêÞB¯ˆAɰ•Át SÏɾQèôš‰ê­TVý»ëÎøßÊ­íДu6d`eá.¥ã_-·¶ÆBQÔdÙ‘‘é—R½}Þâî¦ä\qðÑ’€0d#£N,ás0 EqáÂs^×B~éÁSenÎßtNé±ï‘Ý#)éÒÙ¾/á•Þ¶ú;¬e!:[7ÅÇô««b·¦ˆš"hŠ™öTªf—¸ÿµ¯“õÜ´`åg߀^ :$î~ßÈ.N_oÅ>tÛ–þ»l·~ĵ˜ŸÅ%]î+pÂ|}UÃú8àÿ¸Æ¼ë¯^=Ëqÿ/à ›/ü¿Šˆf^p¥L¸öuñß(®§ –¤ÃŽ[Ç)I°' Êæº#Q–Û9ߣTŸ“üv?"„bn~P‘¯;ê0ñƒzE\ù*Æí³ŽìD6Xqc”‡Jà]Å×¥À1,$=¼KÎè‰r8ñ¡ÉŠèŽc« Ï<ÙHTTž¾¨Ö.íý;'äß–|JÞÓHÏQy ô:¥‹®Qò_ŒCo³ ÆZŒÁ8ͧŒ¤þ¯Ð=>øBïÌQ¨V†4 $ˆ§)–t©àâßøHÄbBEõG*«¾Í´ø×Æ-î‘2ÒbÙ‰ÊQéR~1ñk{¹-&-‘8™ ¥•èÏAôú$p1é[÷MŽ;›@ËN˜ð|tœŒzTÓ :=ŒÈÇ~ûñA –Â8%˜vª´ó&¹Ïs¦¿u扲7åßuX[6Ì6Ñ %«SdØ9ÌË©JÙ6A¶ -Z›&ÁÎf]JÅêÒ­i¢&ˆš"hŠƒ÷™xä9à¸Ø¿Ü?é'ŠZþǧôcù3fþºòO“–º&Ø·—â’ªw‡ý§Ï÷Èê>-ýŸ-?Þ,·þ?ñ‹¯Hù>…íS nV ›ð1Àïm?fÉóÎ_¿÷“Ì]‡úß^:Ý`z¿SÒ}X5…Ó‡§†*öuþk׿õšŸò×1ï^ç÷ÚU¿·ý(ùا‚¤¦ˆš"h‰¢&ˆ¨Guu§~ÞvVKg‚ãðì1;DO¿†W¯üµ_ókÖÍ‘ïã/¹Bÿ¡s¤ýa|›Ñÿ½^ÃÆÃkE(.iWZÖ=ö¯$Uo$ßeO†úúw«$1˜oí—ܾ ¾à¨‰úÂô³­›M„a´öAöÖ5TuÐçÇäÇøÎƒÈjæ®Îj¦èª‹ökœ÷S»9 ‰ûU²ÌLaq)V¢­É¢&ˆš"h‰¢*Ï#¼O“dícÙˆlж,ÓΘlbe”ß B„gÐ,­d§¯ÄØbrKT´ ¯]¸ˆ¶³¤ú‡¼€ÚCÈ6¨{G¾§‘UÎé¢äŒŸ@Ô='Ø }E¢_LýÇÙAηlÍ2úL³96QS 2CO†ÃÇñÚëyS`,Ë»ëZ˜‡‘*ƒÈg §s÷ ØŽO"¢Zmjå¨ d³Ü$˜€Z1Œˆ`KÑÚ¢§,VñÜÝ·vfàÖÀD‡”¥\€Õg¡ Ï-ë{ŽÂdzۓŒ1gÀʃ5‘'Â$P®5PvŽ9¤+¸9Äqˆ&¢=ïøýçCïan1µ¢ Ã'žB 剠 ge;’•Ýt"xH‘4$<IQºÞä™xÚhtø°Myt¸¬‰&ʼn öL+;*{ ˆg‚Ex+¿¾™D׈Mw=JžÚ-¹”¹Cœ*` cÄ8q3*U˜'‘¸Â1æ,1 ™”¡"ÅŒkGr VHû‡/aÙ»ã'®±¹60Ò^ì‘2ûªÉQDU * JÿœÄsØö¹Âb¢·X»žÜuRä€À Ž-:ŒˆÄâ³%Éi­¸’k‰q# 9ƒ€ÁMhò«¼¯2¡mµ©Ã¦ rÊc½ÔƒÖA³c¬æœ-ÈEzµaðRs" Û·jÔæaÙ°˜ãôSévaªu«ò¹vpÒÂñ$ aô×ègs¦m¨*rh‰¢-%Íô:2Ò2p̼²['5àŒc¯ ¤‘îj1… Ú/ª¸ÄÑ7zk}»Fà“dÄño!Sý œ–‹—E³Ì·€àþfƒúˆ¨˜²®¹Qghj|_ lÆÃÆþ°è ?é’Ÿì Èÿ ‰é²b9‹ê4a·Ù0ؾn˜Åå6rÎO0æ7®™>n †/Ø„¤Ñƒ°v”ò‘“ST[&!cUÃéj;[l–&Oy|o{…BLysCížo˜"F¼¬ðîÙìTàå_º«¬®K»¹vFf1ÂGQ¹K*ý«qí-ÈÚˆ€”±ˆÒ ¦qΟba–8­tÉ8þ€Æ‹e:3/êëëãTȳ‚çÄ˜Ç =ÀÞÇÅ ”§Ž1¹FiÌ>Gs ’w.j“ L„MF48‚ÂDÖ¢’gm;q&í鋚€L…æ R†±u0º¶ó»`®hs¼jþø½2Eu¤i6Sf‰ñ¢œ‰Èox”‘ÁÝÍ@6ïÅš\=Í»‘¤¡(ÆŒ Hˆ#:óQâ–ÇmrÜ«ÆR«‘ dMÊœ¢#Ò8,Ÿ£uq]FJ¬?0©Gäøþ Ëã‡á‰'Ø7‰Ñ è!¿Çòµx5vùSXþgubR/8±Ë‘«ÄÔ‡ñY~_k~1 †:p!ƒ§ÀPð[ÜË®K~AQ•cd­0¿S»‡:€Ó"×!Ëæ8ŸÅÅ8è÷r^M 7]˜ßMSí¯‚!(ÉÀ  àNC‘ú϶͂ ã(±5 Œ@ÌñPà¦Ú‚§&ˆš"ä¸ÿæPWWI—ã¤Iž\Ž¡GÍ–Ò9XŠäsU¢qœ2=ZF1Á#\2=M‹ß”I™¨zKŠù³ÀB (×ìþhžW¨ê iäìO4š¹ÆEÔÖ·Qqǃ7˜ ¼_êÐëíK–s«²˜)ŒwÔZWK1cŽ4q!I)ÞN.!Xò=¯g§gpŒ žL´’9}Q ãTe Ȱ@ |ËÛ|¦"Ó:£¨Íé‘v:e!̪ÄIq“QÔ¢¨Ç$cÀ¶Bkì^è))š7(l‘(äV)ýò¹ÌB|ÊEg'ãsp3¹¬lÆ=fu šøU²zeoo· t©&º8Ñójã]ôà2;¬Šmµ¬0ÂÈaØÂ™>®ª<‚D{®X“íF÷TP Û„nŠŠŽg=äpœ¤ÊÖâmB" ¼H,I0^1öȵN¬äÀ=1»· ³‘‘ C€È †”½ÑA§(¹-]l.’!k/«rkÈyæ ˜ég༙æ‡CoõU„vä7VÜÄnOb#o79Í&艶[›J2„Lt‰7¤VqÒãD!QCžª;cÆQœ„µ¿¨ÒÔÇ\çCQ–'»wPC—3=Ùe•¶•tñÂК”9½Íó‡%î/¸|ÙhÓ¢•ŠVð#Zº?ôHôÇš$z´R£i(y±5[ÿó75K–@€:uÖu«ê•j9pZJ.’$[ª›¼ŽòG&¦â ³P^Jy~Ò,Q8™Õº³Âk/p'µ}\×3}ÝÍàc˜‚öŒLIôBˆé.î>ݶƘ”ä$AÜp×9àe¨31û;î«Êš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‰¢&ˆš"h‹ÿÙopenxenmanager/images/add_vm.png0000644000175000017500000000315711636446664015516 0ustar rrsrrs‰PNG  IHDRàw=øbKGDþþþëÔ‚ pHYsHHFÉk> vpAgxL¥¦sIDATHÇ–Kh\×Ççu£‘ÆzY²l©#[ŽëØ‚:uœ&8!Á´%¦›ÐÒEKÈ"!«¨ii!tÑ))]uè¢.ÆPÒ]„”–Òê´ q¬X‘íH‘RlKF£ÑÌ}œ¯‹‘¥ ìâ8\Îý~ßwÎwþ÷*`üô¿éí^ë*u²VEþïr_[Y=×l¬ž:ýã—çìƒVV×9ú•ïŽ-jc¸_|Õ–ž¹6ÿÜ?úä?À/õƒj«µžÝƒ½}]€E«{K)‹6û¿4%I:`^ÿÕïpAÈýJŸüøbî½°ÖLA Æh„í/­6šÊ“çvüÙ‘q“@|/@ytßÁëŸ/°P]cöö2"‚R ã´uˆxÆGÙUŠ‘ °ýæS ÃðááÁQÜ· /alO/JAî=ó·–è»Dž{@ˆ#GæóÍ8vy¥vèé'=S`½Õäî¡ÜÍ`“'[sŒ±ŒìÚÉüç Œ õ’e9TëëäYŠˆošF µV=‰·'×qÃZƒ5 c4FƒÕ £Ø”ŠRãäóÉõEf«ÞçäôÓWp[[$âIÒ„Ü”>ÇŽ(0!tšÈ( ÆV +¤iÎŽ¢ehOŒÎcTšŽ4÷›x’,ÇYCi¢XFk‚HãB .PX§±N¡5ÎàSËdó \ÿ6= Ó|ã×§Ù1w“¬v£ MÓö¶„8t`±Åç4Ö€S`ðhm„G3OèK°{mM×ÉÔÈk¡Ãû|óô,âIÒ ÅØÀ¢ÁZÄj¢h6=iê!ófäIηúC’¤ÉÎŽÃìí/³š·¨}÷%ŠÚaÓd«‹D„$Íèè0×–C574¼Á+ˆG$‡ÜãSEÒ ©§‹\Yþ sëï33ÓnoƒfgÇÊÏbT|IšQ²m -¯©%9¡A;>oWœ{”±Dz’+§yd¯æk{H%À°Xäß×.Q—<õsЈ¤9ÆZœQx§±Óœ& 4‘3DN–ȶةÏðÕ±Œ¸TåòÒ;Œ”Ž3R:ÎåÊ;KFËŠz×Ùþ… º,JT’{å´â' Z·€Ö6l_/£Cæ—'˜Z¸Š+,3]ùF£=Oðç©:Ó•ó”KÇØ_ùúµùÛGlš´ÕÊRõÂÅdGžçÖŽ«€<÷h­QJ¡ÄQI>¦0rƒNRž,ŸÂ¨º£aNø¹¬quù-” †ðì²SÞ»°t{þ5餫Ô=8vøÈ÷JÝ={º{KÔ-¢Ð‘6[¬ÕJ…ˆ•lÔ~™×O`:xfô¬¥ |Z=G#]T*ÇØÉÞ½ ü~úý+¯¿ñ£¾ÁÁB±ƒòÈ 1xnÜZBé&KuOæSÖ³9Þüðq’\óüøøíÄsÆã©°žH¢„Ìé†ÚWÛn÷@+‹Tª«xß¶æR1¦¿³“êÅÙÊC$)Ä‹ˆ¡ÝE¢î  ¤ëPYà£Ú f¶2õõ™+ç{zJWÇØw`d@[kï©,טºòÙÒôÄôÙ[f9H,ß9øez !ükî‡BÈRøl†ÊÜUÎÍüËj@…q¡wÿø£G?ñBgWé!…‚V³ygj⃳ï¾ý§JвÇ^àùÑC|O™r·]¾µŽ¿9Ëìì%ÎL¼Å›õ;ÌoÀ†Y½@mw  T€ •¡oìõîãim)øŒ•¥iþñéßxOrä^€/‚¾øS À–‹m%RÜÆ³¨µüxz`%Á Ïù%tEXtdate:create2009-12-23T11:11:17+01:00|";!%tEXtdate:modify2009-12-23T11:11:17+01:00 ƒtEXtSoftwareAdobe ImageReadyqÉe<IEND®B`‚openxenmanager/images/confidentiality1.png0000644000175000017500000000145611636446664017532 0ustar rrsrrs‰PNG  IHDRóÿatEXtSoftwareAdobe ImageReadyqÉe<ÐIDATxÚŒSÏK“aÿ¼?žwºwcÍ6uzpÔi¨ –8L- ôÐÁB¤uÈcI‡¡ƒ—öX ¢]ôu 4ÜQBHÛÜ.æ<¹9EÍMÓíÝÛ÷Ù¦™§^øð}yžçóùþ^ ¾Ä¹¯ÐAh"¸ gaÂa’0uö±|æßIè+w8zªkjíUÎK’Í^š»ØJlº×£?B@w,¶1AGÄ(¿“Ún´œ½7oÝ~t§ó^I•½TTB+ØÂïéiXpÖ_¯44š˜,ÕE×Öô~™°{A‘=Í×[Õƒñq$FFEd% Y²¿‚Aèd/öö¢¹«K¥÷žßtœl?h//wô46µ¨ØEF–¡@–“AÿÛtIDã]^Yé‰Ç6¦D^°êÚZ;;>ÆîØXžLÈ2¬Îd©X†¤ÊØûòr&âp. ‰ìhiéÏYMƒ¶³%¡ïoCdD H²Zq¸¼ˆ*çe‰s¹€‹W{ïõd¹çl¡2Û›ä4ä"")”‰Žœ€¾¹…÷^ØG¾òÚ¹NÛ¨“×£X ©HºFÄbü…D‚,@0£pÚ{^ƒ0õƶ6¤dÓéœG‘S‰d*†b)r¡–‡ÏùläŒ ,¬¯­jF·%y UFPò^¥"²j³”¹е†úVD£«çrÉ`0ÐŒFTz½¤‚Ëç.)$`…l²‘ˆæÇH3B`‚sù$®¦’I‡, uÕºÑ`Àáw¾x< £)O6Û`ì~ ¥£óó³)ë$ðó~åàq¿PíOúHćä§aÊß óýg`un¤e†¹¹™Ô¬Ï÷¹°§ËÄcpvÆD¹erº®I¶·¹V!A[ÿ±¨)ìø¹e:»ü ?‹Mh¿ý×:ÿ`ñ"þÙþ˜”PIEND®B`‚openxenmanager/images/suse.png0000644000175000017500000000170411636446664015237 0ustar rrsrrs‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYsHHFÉk>6IDAT8ËU’Kh\uÆ¿sÿ÷=¯Ü&7™<ƃc¬VѸ¨EQ7]‰Tp“.+Bšu±P·Jv‚‹Ýh† ,‚m‰›†ÖtœLšLrgæÞ™{3sŸówÌÙ8ßï||Ä9Çéáð’–þÄ¿ÿ¬Õ«•ÀÔ ÿŒ«Ï=L³3]€N]‹ÿ_BÞ•n·¾g}÷æGQÐ?'#=pÜsD•îž{û«—ϼû“Lzt¬¡c~â*ßU¯^Ù¶6¯Ì˜¯fì˜ÈIœ Õ±°aýÖ)š³×ß+.^WY&8ppüXýâÃ?ön~ùÒä뚬HààÀñwaâNe­w6ÿÖÇŸúäkA€ïAñöî—§‹Ó—Ñ mzܶ·ãÀsvmDqÓcsÚݽ—w¼Å“ þ²ÖÎëº8%«}´ZðÝŒË 0 á'.ÂHfŠ MÝo¬/¤g¿`ÏÝšI¥ˆÕêq` NBÈ”†*f𽸃 ö é*Br¢Áêîã™#ˆãPiû»Hº%KPApÛ6"8† ‘5(ªRö¡ö}DÝPDÍZ¥ÛC†e! 8€áa©”ŒlNÁA݃¦KÈ ¨ "l=ÙCš™5ŽBœ2^»Õõ¨¡ëˆ Ðd¥ñ!äsHIJ&Ì,t¦q†Æ~Ô˜2æoÿfóóëc4¿lÇÊO`¦4Žœª#%iÐE)IENIADlÔªN^YžÍϯŸTIߟ[¼æUó«¿W7ÐŽ\0bИ)‰Á\üúèOØÍÕKsŸ]S%=>ÕDØiþ=ñí½«ŸnÑÏ—ŒÌ¥5"ÚžÏí:oNò7–?x~ñó‰ÁgvNªÇeYÔjÙ°šûêÚ½/Ü©®^èôëeÈ ùG/ÞüåÂÙ‹ëCƈo À2¹a ••ZZZ’677UÛ¶Uß÷Õ8J¤>OdÊQ‘ ¡$‰‘,˾a~¹\öBr•J…¶··˲X³Ùëõ:‹ãX "8ç\Å$ŸÏ'¹\.M …BR*•ø¿ß¢g·4o‰ß"zTXtSoftwarexÚ+//×ËÌË.NN,HÕË/J6ØXSÊ\IEND®B`‚openxenmanager/images/restart.png0000644000175000017500000000363111636446664015745 0ustar rrsrrs‰PNG  IHDRàw=øgAMA¯È7ŠébKGDùC» pHYsHHFÉk> vpAgxL¥¦IDATHÇ}•YˆžWÆç¼ûû}ï,ßL¾Lf&™%Ëdc$¤5I/1T1µ¦µj/ªƒ±5Ò ¢B@*â…+¶i‘"’´¶"¨¥Xk››¥Ñlؘef’Élß¾¾ûñbF µzàÏùs8çùqœçüñ¹ŸC †g1jëú,Ý—B_B©¤àÇÍs­0:Sø‡gá›ðÒÃÿ­#Þ½ðÅ_‚!;¶g­ž‡:íÑeíU«#o™zVèÒ@¢âRXõ'fê—Þ(µn>WóÕ›†FøìgÿàËG!NÉz¦»¿;³þ±Œ5Öè4ti k&–æâl=ƒ­[ÄI…éêŸK׋¯ÿ¬Ð,}ßÐ(>}ÿ4µ5_y”¢×³»¾×•yÿ¡˜îŠßÄ[$*F¡èXgwSó‹(•¢ë}ãN‡ÝwGßiÕ“;ÕSGn<òD nÖîxܳÇ÷—ÚšQjÏ£T‚B€’Ã{Ég¹^:¿tw‰R)IšÒíŽÊœ»js=˜¨´Ê¯ßù­SGA{ôEÈÚ€ÿ`ÆÞðíÙFbVüV÷ŒÓa÷Põ„l\¾)4®ÏþûsnY³“fXųpÍŽ åöÅÖÉÉàO~ $@­==ä˜+¾Vj w¾y“( ÙÒ·‹Ïo?Ìpn~Ü"ˆÛ$*!U AìãÇmrî îú˽!¢$ ÕXæm“«º?øðx?ã Ô¤ƒ©›{•Û<]›&J¢4àw–Éò%>³õŒä6Ä-R/’ùìJvßC¹5ÇùÙã‰O˜„IÌ`÷ÎU]nï}O>§„¶qoìxvÏ7+¡µa®1ƒ@€´ãW o³ºg3»†ïa¶q••k‘RÐŽªì{ˆR{†7¯ÿŠVÔ@ ‰@'!†t©ù×ÝÁ‘™—Å£¬öœÑß^+‡ëæÓèRÇÐ lÃÄÒ ú¼•<¸í0=î —N Kƒ5½;ð£:¼ú<°F˜Ä4Â:õ F=¨ K¾LR¨¶Îß«k’A0{ÛQŒå·±²sCÓ05Cj\+þ…eÉ¥¹×Ð¥I;Z@“Y+ "¡6P¸”ÛsL”.bê.]ÖP§&å.ÀK•pcë^³]Ã÷ @ A”¶ùÃåD4’³µNîÞpCsP€R „Æñ‰—˜*ÿX%€”R躂P[š$Jc~}á»»ú#CÃÖ ,M0Ú³“5½;(5O“¨,cùÝüéêã\+'Na"(µª2FÇD ¥R_OóšH*žey­”ªÒ «(i ½»¹kÍ!ÊÍsì=€ct±>ÿ~sa?W ÇhÇ‹_ÃÖAJ]¦¾ŸÆ³2Œ™Rªu£ÛÎâê1À3!kÀúü.>±å)þEÞšx ×ÌàšNN¢î_`ï–gظü.²&dÍų®9ÇCÜŠSÞ‘?}‚R”ÔŽuÙ.YËÀÕÁ5`]~ßô ÿ"§&¿Š`C M ˜áôÔAêþyîÞô4cù݋⸦$çt%Õ3€)ùè×~TY ~Y&½´qݲPóÿÊ™©ƒÀ<ží¡k&†fáÙ] œ½qZûmÖ.Ûƒk€£C“ÃÔÒ–•^ìve¨ƒ¢pÚ1fôyÃÄISkòÎÜLctâÙ}˜F†î!xîaTÁ œ™:€Kl&†¾‚¹ú­ß·"õŠc¨E/ò,¢FPÿaÕ/œèrFÈ6J•‘Òǵòdœ•da„¤ 댒u†ÈXýh2UÆ1 ºQê~õrÍ/>î² <;> ¦f”›aõ˜Û{³ý}3ÀÒ-\»×ĵIe@H!5„(“¦Á’½ s«ÞžžkÜ8´eÅû^›¯Ïðä¾¥<8y¶~*ųôéR«rÂÕXÆêvûEÆîŶú°­ 3‡¦»Hi ¨”T9”Ûu­T8=S»y ©WŠÍYõãûÞ•h§ÂÖûSÒ˜¹VÒzµÜ®Ú‘1 ÉÞœm.—ŽÙ‡iô¢‰ q’Òh5˜©”ÒËóSÓ—®>Uh¿¥ ΢£~²ï½3Yô¥Y˜Y´]_`íÀã£ùe½wä:{F\»3#„í Þ.W “ó……3s7ƒWßúçJ“„K:ñR%ï0n‡,•ô±û·éêÃQ/Ò˜>O£rE‹–¤@t„GãÁ,w7%tEXtdate:create2009-12-23T11:22:26+01:00&êt%tEXtdate:modify2009-12-23T11:22:26+01:00WRRÈtEXtSoftwareAdobe ImageReadyqÉe<IEND®B`‚openxenmanager/images/prop_stgvm.png0000644000175000017500000000150711636446664016461 0ustar rrsrrs‰PNG  IHDRàw=øbKGDùC» pHYs ‡ ‡“@‡S vpAgxL¥¦ÒIDATHǵ•OkQÅÑš8Emq!¨;ÓD0ɤMw¥\ˆâRÁà§P\ â^°R\—qmQi&©`gº/”bZšÓèÜëbÞüImlP|ð˜ûÞÀ9÷ž{î üç•J=~x˜ÿ Ÿ»?§îÝàì½Ùsž¿~í&Ùl˲öêõz€Ðlnáû>¼{ÿ¶vãÆõââ⛯€ " ›Íâû>ív;ºSÕhƒ¢¢(ÊÒÒff.R©TX__ø2:j¯V_{ ,Ëbaa!FAáÖí[  * Ê²ãP¶m:™L€¹¹9ºÝ.€[­¾N $Hfm"0‡ULÚ6Šò½Ûåé³'ì;ù|>&Pa¹¶Ü'•)‡©ÉI¾mnòñó'RÀé3gÙü¶q0A¤u˜q¹ŒB ½JTM³¹E&=Âì±YšÍ&":ÁêêjÈ„¢”m IQœšƒm—CÄç¨eñ£·ËØøIܵ¯}X‡tE¢ DƒÌE¡dÛ¨izúHšôÈÙsçÈåòÃö ‡ˆi0ŠãÔP %ÄòO#© —.ŸŽ +(—m¢}UŠ1šîõ»D­V Ï[Åu]\×Åó¼TQ¡^¯ƒ±Jpßh4†·©Š²°Ñ"Æ=Åb1¶¬©¦X( OËMD` 8u'–ACòð“d¤ªÌ]¹:dÆža\*Ùˆ*ˆ ᰉƓ~P¢Ø[óð¼5<7xª­5j¥±b 4_”(Ž#‡IÏŠ"{Fw… t:íd²²— Ðé´ÉNLÄ–DiÔë À@žˆ\%z7sñRˆuè’$8»ÈøÛTS,$¾E1x4ñz{ ü0¨LOó+êg’`·µÓzõâåó;ÿ‚¼½µ]5Ù+ôÿôS@8œ0ef€ÃF¾Ã&¡”ÉP» ´ÄöC’ÿº~êÑ+º%’IEND®B`‚openxenmanager/images/usagebar_7.png0000644000175000017500000000066711636446664016306 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEC~¯ò}¯üv®üvm߯üuB‚mñ nàn×!A–¯ê…¯í¯æ‡mèA‹ ¯øyB†A‘~~~qqq¾¾¾ççç‚‚‚ÙÙÙzzzãããéééÜÜÜÄÄÄßßßëëëvvvËËËsssÅÅÅ¿¿¿êêêmèménØ!n× pppÓÓÓ®üunßmò B~ÌÌÌÿÿÿI‚2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïyIDATxÚ¬ÑK‚0EÑEñ* *’61aÿ›3 {Ön½ª³€ã,A¢ƒ¥Ö!ŸBˆRˆ†§øçËøœ¿S9ðïûŸ»soç8šµ1«ŸW뫟ˆ”Tr³åóé|׊ÖúÙ”âo˜çð¿ÎÕL#j ³3IEND®B`‚openxenmanager/images/xen.png0000644000175000017500000000106711636446664015054 0ustar rrsrrs‰PNG  IHDRóÿasBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<´IDAT8•’?hAÆonÏóES(Z˜1ˆF‚þE6‚`ÒÚ¤°N!,D’J‹ÔZH*ÑFE¦IRzhPO9 s\rw»3{;c%d³90Õû¾7ß{32ù~µWZÉ$¸qk)ÑJ¨"òÌ5×f‚ß?ëeàA7Æ-”€)(ü ŒÖÿiÞÊD`´òUD@‰`­C)!§„¸cqiÙ2&bû‰ãˆ“‡ŠÜ8sq1÷ñðæ œ5m`´ÎÜžSÂÁ½yÆÎ÷Ó5å+¼˜ÿNF$6ÁÛàÝr•ò¥cܽzœÅokÌͯšNFyÕ¡ÑjÓ·§ÈóŸÙh5q.«ó&ؽ+àþõÓ䱄‘æÎ…–VV1Ä× @)aôl?#¥>Þ.WÙˆbƯ Cb0:öNÐSÈsoô›­'¯1„¯µuÍ6‰µ;s õ¦áñËOÔ›Xo*™íÿCŽŒ=JUD 0q‚õmÍ“ ¤~£ŽüOë¡ÍO»ulc6˜¾=\›[øñjé˯ËíÈìßÑ"BoOq}dððÇ[çŽÖþ‡BÙü~Ö2ÜIEND®B`‚openxenmanager/images/usagebar_0.png0000644000175000017500000000042611636446664016270 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<EPLTEsss‚‚‚zzzëëëÜÜÜéééãããççç¾¾¾qqqvvvßßßÄÄÄËËË~~~ÙÙÙÅÅÅ¿¿¿êêêpppÓÓÓÌÌÌÿÿÿ¥ÕÌjtRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ@æ^DIDATxÚ¬ÑÙ€ ÑàžÀƵÿRÁÂü¼ßÌV³YÏûE0¹ßһN öÒ¢Bî$³Æ o -¸#— QIEND®B`‚openxenmanager/images/usagebar_5.png0000644000175000017500000000066111636446664016276 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEn× mémènØ!n×!¯üu¯ò}®üvëëëA‘mè~~~A‹ ßß߯ê…ééévvvãããËËËÜÜÜÙÙÙB†¯üvqqq‚‚‚m߯íA–mñ ÄÄÄC~zzz¯æ‡¾¾¾B‚nà¯øyçççsss®üunßmò ÅÅÅ¿¿¿êêêpppB~ÓÓÓÌÌÌÿÿÿŽËÆÙ2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïsIDATxÚ¬ÑI‚0Ѩ8 â΀&hº÷¿œñEmÞ ß^ÂÑìRc†Œƒs+çÆ LN]¨eÌ>ÿ Ì£hÇ#ÆÕûµ÷9[ÛX[VŒì:/¨‘ ,Ïwè^0 zQÕCd/"úþ`PdL‡#Ÿ[ IEND®B`‚openxenmanager/images/shutdown.png0000644000175000017500000000363711636446664016142 0ustar rrsrrs‰PNG  IHDRàw=øbKGDþþþëÔ‚ pHYsHHFÉk> vpAgxL¥¦ÈIDATHÇu•kŒTgÇï¹Í™ûÎÌ2;;ƒ\º\»Ë¥…Þ Æ•ÊÍ[¬¡[PR0^èõ‹~05úÁh5ÑXM) ÒMH«Ñš´Ui‰EаÅr-n²À²sß™3sΙóžã‡5Xû~yß<~¿¼yò<Áÿ=ÇY©ÿˆS»';ÌÔi³°rq6îŠ'…¢U·êï^¾]|nÒº8JeU"Î ç“À÷>Dÿ[Ø_ÍÁ¿gÅêõ3·÷«ë–ß]Œr‰žLo$‹ „h7Z­z±r»=~ûüù‹7Ž8ë¼ö¶ûêØW“«y¾~üì‚ÍæC¼d§´ot[oZ¤{ÑÀ܇Ò}sÚìYÍA,B€Õ„Ryã:•+ך#çFÿñòë—C±×Ö†o¸ûÛï~ÀTÿóèe#!ß‹~=WÿÚæ9ÞO—’÷&»Ó!¥+ ‰.ˆ'§oMÛËB±,¢¶eä‚ÖÜ»Dm°[Ö9ÓÈœøóÜIFÿ+ˆò.žþåLiÇštãÝ%g! 3ŒˆÅ …tæöA<•”ŠP-C¹D§\Æ·êñ¤W[øöévbVÈ6P,³5YZ»*^û®Ö±ºVÛ¶‘õÚ4¨TÇ™Ecà:Pœ„R Y­Ðj6©5š¨+u¤òõñòg+|F|ðƒeÚéüöôØO24îi»4!0ÒŒ¥ËˆénÍ_AÎ( Öû—©Üž¤V¯Ó²B¢ÏhüuûÖÑþû5õÅDŽŒ)¿¸P¯îôܶ)¥ç¸è=9ÒO~ cÓ›02K@z06„Wá0yòµÉI¤ïãK‰ŽLGyõ g×°²³ñ©ä]ZcP¸VÒq]:®‹íJb›6Y»_J¬¿¿…=q|{tûÔIˆD‰m~œè#b;’Žëât:(V,/¦{Åãe£9VP¥3ÐqÚ8nÛv1-$÷èЇössïsÔoßFz¾ïS¹ÌõÿÒ‘!„®“ß¾ƒðâÅØ¶‹ãv踪´û7˜ÿ*hýF¥[õÛ½ŽÄ @“0cðaBÙ¬÷.ríÀ>ürçÌ;x{žE¡2|šæø8•g~9°„Øâ~º7|šñáa* àä>¦ÔsZ ÝT#pcváRºBr`¡iX—.áÜGÍâ$­½»!éûHÀ½v湳ė,#5ІFÃó„|/(AZ ¤OÕ÷)¢„¦MO_࣪*ª¨º>] D „@H €¢i(Š‚”ˆù>6ÅWµª¡èÍ´€¨ª'±FF¤$2fOETUEÓ4t]G×5T!0³Y¢ ø>ÍÑ+tœÐšh-QQ.iù’©…nd„ nh4ŽëV‰ ,%¿õ+DâqLMÃ4 LCÇP5Ìh”Þ/m#¶ü^¼©:µ£&®@Z $̉¢–™PÇâŸïlTFWôÐZ®+‚p8Œ¨×çrDïYAd`)F"‰Ö¶ˆf"9k6©-[éÞ¾ Q<2ÄÄ‹écToÅÖ iš{jgýGgé­/è^;©†ªP=¸ŸðŒ,ñuéÚ¶íz‚‰[ˆ\%—'’©WÿDiï4@7BD=‰*ôæ=ýÆÅÏ–Õ#žG%µfòA­|_ÖúU#d†Ñ=ñÞEÂÒCí- ²=(…™(ù"…Rï÷/QÙ÷í[7ñHßGP3R'‡ï{úfצš PÜv¶™}¥twÈùD—!’BÓ1Ãafˆp«…hÔÁ¶¡Ý‚R F.É·PNŸÄ¯T°]OJ4|ZZìÖëÚ¼§þÐ÷ÌIŽ¥§—Qžâ”›g¸í>S> ©M7HÎȢϞ¹<Ä`†¦—]» váØˆf“öÔH¶ª§÷é!±à3žS=9-Õ·I$–øUºÒ”©Y¡` ;JF3Doò3¡7½H&Aúв ^C©U‘)JåÖß䌟ý5ÈínIµ=~õù;Íiü“û3ó:¿’}飯r.£û3"™®¬Ù)3º!•šEVjeää$åRµq©ìrsßÿ­œ¨ß+Úo^}ù£Cÿ±þG8<õ;–vv¤ë–÷eÃsf $ó=ùh:F¢U­·kãöÕ±ógG'޽P‰¿>üëߌ=±ísì»øÇéÜþ(Á´%`ù•'9“1²«kkÁ*<0;•M¤q!`ªÖlž«÷ß  ^Y9öM†»Ÿ‚WrBýÿ 쪼€%tEXtdate:create2009-12-23T11:19:49+01:00w*ì%tEXtdate:modify2009-12-23T11:19:49+01:00w¶PIEND®B`‚openxenmanager/images/storage_default_16.png0000644000175000017500000000152111636446664017733 0ustar rrsrrs‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs»»:ìãâtIMEÚ 0 à¥ÿÈÑIDAT8Ëe“»o#eÅß7Ÿ'Žc'FÈñ@„!,â!¡ ÂÅŠH »Û DGh·¤CûÀ"D‡è#4¤‚ÎA–bç=„ØÙŒg&™dž+pê{ϹçÜ{…Öšÿ"MSÎÎÎð}_ ‘Ïç‘Rþ¯VL’$akkK‹E²Ùìiš®h­-Ã0¾ˆ¢è·Ñhô°eYB)5%8<<ÔAP,Ç/%I‚a˜¦ù¹b+Š¢ûiš¢”r¤”?{žw+—Ë1??/„Öšõõu]«ÕBð¯*J)fffBÇ1iš ¥$ Cƒ+++B8ŽC>Ÿ¿lB ”" Czv‡;ßܦYŠÏÞý­5ƒÁprr‚‚b±x% $IÐZóåwÉ>$©äëxž‡ïûìííý3Ѥжm:“´•Rär96ì_E"ÆÞ¾þý~Ÿ^¯w)¢&¾<Ï#I>ýúŽý–/óÖsòí/÷xôi‰>œçÉÇ®±1Ú`8bšæt€r¹ŒeY¼Ò¸‰ÊÆðÅOóÈãYŠç믳¹¹‰išÌÎÎ^Þ„œÎÁÁÛÛÛ¼Øz•fõYª‹yBéñÌJÑÑo\¿eYÄqŒïûÓ­L,4 °m›×nñ`7â½;7ø«wÆ ×ðÜSö÷÷‰ã˜¹¹9 Øf$ ½^J¥B«ÕÂP’àdÌý»?"¥ÁGo~B¹\Æ4MÂ0dww—L&s5Ä¥¥%¢(¶mÚí6÷ÞÿžÁ`@š¦H)ÑZãº.®ëR©T8??ŸZh6›ôû}‚  ^¯Óív‡A@\\\pzzJ¡P T*q||L«ÕºúL;;;zmm\.G»ÝÆu]ŽŽŽÇ†A­V#Š"ºÝ.«««,..Š+ÄqL§ÓÑŽãà8išR*•¨V«,//‹‰÷ þrsU©OÛâ6IEND®B`‚openxenmanager/images/xen.gif0000644000175000017500000000176211636446664015037 0ustar rrsrrsGIF89a÷[ix`o}cq~erZ»c¹>Ç3ä?â?æAÅMÀAÏQÉ@ÝKÙIÒLÓXÑbÂgÄiÏrÅAìFñX÷ WùTóVþcìeüa÷kñoó}ûtÿ~û|þ&gã$ië%eð"|ò2wñD|åˆø“ùþŸû*žë'Žð)Šò-—ô£ó­ð¬÷¡ý%ªê1¯÷&ºòCÎAŽØCÐNÜKïVíKìT‘îXœì`ŸíEªíY©èX¯ë@·ô\´õc¯ìh³êw¶îy´í{ºìgºõ~Ñ÷ž©´¢ªµªµ¿±»Ãµ¾Æ»ì€½òÍï†Æð…Âõ€ÄõÃñŽÆóˆÂö‰ÃöÄô“Äô”Êõ—Ìô˜ÊóÏüšÓô‘ÒÿœÕÿ‘Ýø¡Òø¤Õú¢Õþ¡Öþ«×ù®Õù­×ù¬×ú£Ùþ£Ùÿ§Øþ¦Ýÿ¯Øø©Ùÿ¯Ýÿ¯ßÿµÚ÷¿Üõ¼Þ÷²Ûû±Ýû±Ýÿ±Þÿ³Þÿ¶ßü³ãÿ·àÿ¹áÿ½äÿÃáøÁãúÄåùÀâýÁäÿÃåÿÆæÿÇçÿËãøÈçýÇéÿÊèþËêÿÐëÿÖîÿ×ïÿÝïþÔòÿãôþ!ùš,Ï5 ˆ¥Œ%$3†G‰Q(ðQ’-PtàÈBÉÐ…2f¼°Q#M"H “„‹9Ìð¹d)R¡ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFîIDATxÚt•]Œ]UÇ{ŸsϹÓ{{ïÜá¶–´´Bhµ¶1hãDQú¡1?# ¼˜˜`Œ1$>(ú‹‰„_¤*- )j"_Aj+m Ð™N™¹3÷ιsî9÷œ½–w:­XöÛZÉþ­ÿúLP÷p™ Øòq©cªUbõLŒW¢&÷ñS3´Û=Ì”ãTc)J»ð‚ù^ëÊŽ#5.ìa™">}ž9¼åsÇ>3ÓÚ¿Ía²qÆê[m^Š^½þïݧ€G®uÙ\Cñ'¦Z¥sß¿eçá;o¡^žÁ·àƒâ4£Ÿv9óò)þöøkÃK'{_79~µâƒïž=ÒxöŽŸÜÔš-Fš#" Ž (b dy†:ð<Ÿ£Ñÿøñ«zú‰‹‘é.ƒ=¯dQQ€C×ßÕ<ù¹'>Ž*–© M>™Cz…n‘·³7蹈ú¥îLŽr ;„éx\h}i«qI<×~eðž*ÿÞã —_¿õçûOYž_áî娶ؽõör#Ï-ÿžåd‰/¦G9¶ãAvoÝËNÝÍÉ…,È"Ûom˜Á»½{úgÓ_ëvü‡÷~»±kU†,¼³H´Ö%ÎcÔ(8H5eаÖéºH5¡7èóÁü%Ú½³ßh„aÓ>߆¦4}[éÑѾuή!ârF¥c XpâèöÖèh—ÔOÁœst¢í¬Í S¬BóŽâÏùùºÜµå ßj¿1X¤™Bz]f B´±šIªÉ ¨8¢nBg˜Q!Âkj7…°é}Çö>ªÎŽÎ*ƒ!7 ûtbÒ é¦$#ÁÍæW :GÞQÜ*ŠäJ áv{»ï×½.säK·UÀ ¿<é ªh¬‚æ“€ˆ¢ë CP@Ç [¿nk>ÖDŸ A¦A*@ÆfË"Ц ÉUUEG }t $cÁw‰¬˜b€BÞÁ°:Å­(:¯ÊgY΃ˀ˜ºÅÅ$v<<ëiñzƒ™ª@ŒwE™¨ #&Šå*+4ÑI€×4„ ñªœ±n(Z¿(½úͦ ¶”_' °©ê¤hp%¯eS@*{ 2²’.¹§¬(ËíÅON]W"¼œ™€m`7§Â³>&4P¯àmZa¬A nW ¾?¤ûZvVÖõ ¸4Êí¾$ÛŽV¡&íCa4¶•˜ÞÒÀó<:£ˆÎRŸì0†}Íb^ÙÂùÇâšÈÑl(§ÿFE¾ºó[µß츗Bo:Â7™övQ©Tñ}q–20ÙeJ%úƒÏ»?ëÿ2êC€ÛÜ ×SÜÔ’=ãwö~²Ñ˜š+Úƒûgi…»È1ôò5æßŸçŸWõüzñø¸ñ˜ʇú¿«é#ÀØ%uz¸ùéÖ_£E K~‚%e.‹ÝgyÄ”íóy_º~> þï±H–  )òIEND®B`‚openxenmanager/images/prop_startup.gif0000644000175000017500000000072711636446664017007 0ustar rrsrrsGIF89aõnnnooopppqqqrrrssstttuuuvvvwwwxxxßBã„nƒƒƒ½½½á™‰âšŠã›‹åœ‹äœŒä«žè®¡ÅÅÅÉÉÉÏÏÏÚÚÚÛÛÛÝÝÝêÔÎàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ!ù>,ô@`H,¾$k6e0™tz“V9e ÄyG`“ØÔ·Î­¬æýf#³}VØ è-þNvl`a}eŠ-NM… ‘~d€Q2v2# b‹Œg˜™J›  ed&–S2Uš &chN°±³rtŒOU½d¹À»7Éš gQ˜ÉÕ7v5-©-0¯â  7;;Ø¡0¯TÖ蚢Œí2 øå èñJ71(8ï¿tJj4šR£ZŽƒü ‘ª«áCˆèÒ(ÁÐâ]µÂx FÈ‘“G’¨\ɲ¥Ë—0cÊŒ;openxenmanager/images/server.png0000644000175000017500000001176511636446664015576 0ustar rrsrrs‰PNG  IHDR<PEÊSbKGDþþþëÔ‚ pHYsHHFÉk> vpAg<PÜÑ'‹ùIDATxÚí›kŒ]WuÇkŸs߯yÙc=¶“ØyÚ¦!šBH¥ªò‡D´H($¨i%PTBHEˆJ¨¢-!E„VýD©J‘Â#!4!œØ‰ñØÏÌ™;sïÜÇ9gïÕçÞ™ñÌÜ™;¶@ʲ–ö>wÎÙ{ý×Z{íµ†×è5z^£×èw—ä•îàî}¨u( NbânÕ)F Š"€x†¯\Yùý|׋%¥.Ž,&!JÒ©ÄH¡ ¸jDª*u\˜Uƒ Ü»oî÷ð/•´ŠînBx=p P’íW`8Šòð°¨<«¢MîÛ7ÿ» ø®c%<<,Ñ  onáV….À9E£vç>#qéƒÀU~|G”‡<üi‹åÞ«/ŸÅ/ àÌ’Œ|DI«p‡œ#ߪ(µqÇBÙÒª(a]Qw+$²ê²C†ÂvTŸ` 5…'Qîå;*4?âþ+ê¿}Àw½Tij†È¸Qà¯þ̶èk”•“–ùq‹[ðÉ¥JäÓ%r©ÙT‘d"M£Uc¾1ËBsž…V—®“ßîÑ…GfÈ२ ü;ðO¾3cÖsÜ»÷Ò\ü’ßùR‘’ï1Ú›>‹rK£¬2y$¤zΑ1%vo¹–]CûÚK_n FWLx?›‹,÷]è‹|ç Hc"}?ð²gî”eòH„4²ìݶŸCWܶþ]¤ü Š¢ª]Û3bmÀtm‚'Oü”ßœy—^`ëŸÒ/Á)ào/ߢéÜ}×V_]ÀwŸ*µô]"|5ª3:õ\ÄìqË–Ü(o¾æÝ\µíI/ 1ÔMdˆ\Àñ‰gyä…0Q;IÿU[nðñ³Œ©ò ?%?øÊž‹³ò¦ßõB>oáka“k&žŒ¨Ž)WnÝÏÍ×߯piW Q{ºR*Á09?ÆŸýoNN>Gq—aøO"ÍQU>NÄC^îݤ¥½ÍÊrà£ILR^|Ř:ƒÝ¿óùãƒd°° :™“\$ äÓ%F÷RkÍ1~î6P2CfH êzþ©¯¯àéÃø:ü³‹¸eúËÜIǾm‡xûþPÌ ¬;N/†2É<#WR®ãü¹ P!3`FDØa<9ü†»²_}µÙs{fSÚñõo½¹¶¿ïÂqµì›;e‘ Á¡«oe[ß."B{=Šj ÂK$#ÊÑ)Z,Ä ‘8–9Ð6»HI’c8¿B(ê¸ ¤ŠÂÎÁ½½‘ÇO>ÀÜ)KâzŸñ¸ø  qÙßñXuúDß·0¡R/;®Úz€k¶©]št4¶¢ŸRÁO ]àÈñAèái µŠs 6fç Z¨±¼ëM·“K”ˆZJ°³Z].7ì|ãsÇ9[~‰ú„“ÜvyÊ÷ïx¬ð½ÿ|ÓÆ»W gÔñ‘¨¥¥êKÆ/°ç$ýVm[ðSBª$$ó‚Ep¸ ¢?5Â7ü#C™]‹SÕRh&«§ùúŸÆi„øßW¼,$rJc.οc·l²ÀÑ·0õüæÏ´H–¼’Ÿ’ôbå ƒÖ?/ N÷«ã¦FY ëpÕð†Š;]€Õ•ˆdŸ#·MIÄ¢jQâÒˆ!íÈø2‰"™D‘ì"È$ ˆ˜ ¾‹ŸUr[•Ìßb]D¤!Ûûö0:t5a]i”uÜ$N÷ßñó†–Û8J«ð^²µ1ãȧKìþD§x–ìdñõ€Ó@½& kŽÇ¾¥°UHæ:–Y-$8Àõ˜KÇÞA—¶‡ŸV Ã/å1켎„IѬ(q­Z½åÃ?Ë_’…}UÞµH5ÇP~„¾ìT"rƒ†d6ž†º±ö ¶Ù-‚îÆ~JÉð"ó# æ·ÔQ‹”*ocƒ@¼.`µÚ¯ŽQ]Á†K£#¤û S¤‹%Ö²ôÆ cÅôОZRY%7($ Fú®gˆêŠ:¨Õþu-¸®Ž] {¦#È1ÛN" ù¦ã‚ÝI×óšL71²%Cж“Nd › $òf° ˜Ú´…?t¸ˆ*¥dC¥/3D>] Û'Ÿx Q·.»vÙ{”Ö®ééJFÙ>C1W¢/3„ g)©rðC‡‹]ûè Ø:kÔñ:’À C…²…éœÄÛØY:.ÝÜžÚ\Ɖ´’ïK2T+¸„:^gíŠk=—Îã8èB%ée, “ïó¯½UÓ‰Wë’•ÍÌÃyYê[ _ò(áÏ$qaˆñä 'Þïî°ZÍ«È.)Y?G©X"•‹Ä} 3Ñ8ç['ã£]Z ¨‹R'4Ãa°þd µÆÁ Xr*2œÛ`ÇU2(äÓ29Ä-ÛÑP–×ÞÃÛúïîü´¦; Ï$ºΧúùà¡¿¹0êëêV¾õÈ¿p4ø!,&5 *¤3Å\‘‰&8GF„-ÝúZ°UI†b¾H*mµSì·ž$HHª#ÓE‘ $¼ä†ïy^‚ “›X3‰„¡¯PÂÌÔi¡k˜^ÏÂIUõ|“¢”/ày¬èȬx~hÑ»:™W[aF(rñPy]µ·à”*ž1†t*'«À½Š`ÛÔt³„Ôð$½ô£©”çy„.ôŽË­¦®áS-B{«ÆOHüp¯¥€Wžê®L9:J¨ Ëäp$>¾çu‚]×ÁÕ°8‡#ø¾€¬œø]¼Ñô*“ˆê[6˜vü¨¢²Î`ã·Ž^`§TQÆ]Äž0 i¸i|“Æ[Œø‚ 1°f_µÖ,ßýõ¿Æ©¥®Zâ„—ÆŸ¥´×ÃÁµuh…!ˆZDñþ¸ ]7¨»¶R8bCnl4C $#Cøí9p09DèÎ2eÿ/Î ´3–ˆˆ [çå™anÝñÑ®€aÇÇ¿äΓôK€½xÑ9¥Èívìˆ=f…³4šaçÄâˆ"]ÇpWÀ¹Q}Žg4ÂVæë^ZüT‹†N’¢€'ú“ýô'—ÜÜi“HDÚ@ÅQo58sfý#,UÈeRì½a”|.ÓuÃ`)bÈ¡Ã:ËLebEy&W"Ú4àÙ2$}žÑˆj­Úê«6ÒœFZA¨bð㳢ŠxE¤=…Ho”Ò~×iŸ.÷F"B­Õ 2WG#ªž™-w¿«4~²sœPÇX«a™©Öb¡Œ‚C p´P”Á-‚ôÄ`ð0bº«UˆÁˆ·ø}¯<·P§V PeÌ9N<øÉî'.ëŸ-©Î¨“碦*ó„;,)?IïóoQÙ`³¯ýfpgQÒ9U&fçâ]ËçÄè̺JÝ ½N¶MÊåsŒÈE\7´ßø«ÁÁ73­1¾üô‡ñHâlçBK¼Éî,Aþ sƒYuR!Äóí©¹ó<9~”¹…:ó§QSÏ!|I„J¯ 6X5ª³÷×§ô3µ³j¼+ž™|‰ÀFÜzi?¹ t&‘æW\E#hÅ‹‹6³¼t)2©!²©ôª '‘³›ãé‰i„-jg•ú”:÷‹ñ׿½¢ØÝúYldGUù//Å[úvÄ‘xwq;7 ]ÁÖlìÆ@{ÑΆÉG¼ÕÑWç>õl«Ê Ó/óbeŒÀ†ÔÎ*“¿Ž°-~!Â<ßûÉß÷~S~Sw-Oý8¤”HΧwê˜ ¹©YѾdQH„Ù`žs ešQH1™%ã§ðŒi¯š…ŽÝºsœ•Åßêa“£³/óÄÄóœ©Mâp,L(“¿¶DuN |jîQóÔc_ïé>ÚŸúv–MðÄŽÛ7µf5Ÿ0$óB`C&›3œ_˜¦n›¨BÚOâÏ,%+²21i/0"µ”sŸ?ÃÓåc¼X9MöOhL+¿²UøT`SßÏFœ~¨×ãØE‡Û<½ù³4t¾ÀŸ«òÅt¿ô ¿Î#?bCÛ…´—¤”Ì3’b ]"í¥Hx>IãBä"BÑ´-*­ç¦˜mUiDMçàjçÏXš³Z᯾! =ò¹ÍY÷¢¼ù3)l$I#z*ŸñÓR¼Î0x‡Ÿî§x@v!é%Hš"±G.Äjç`lé}ˆš0}Ô2ý¼#jꜟw*_ö| ù|ë¢äÞ´KwhìaËèM¾åW(c.äúú¤óJ² $rÿŸ$#‹—ňÔÒr-i|ø½x1¼ý>@£¬œÂ2sÌáŽ'ð D‚G¿pq`/ 0À™ŸGŒ¾Å·aÐzÆ÷ý_ª2ÜšÓ+kãêsŠñãû—&Ù¾>¼xë]­Ùµ`aÒQ>b™:â¨Oj€ò?"üe´~èù¾ýÅ/,\¦MåÝ·zlÙï‘Ì2¤*ŸPå£{¼&7l(ŽÒñÅS/)‹3”*Ø@iÍ)Íe~̱0á°-œÂ)þCD¿Ô)O±œþéæÔå,kÕ¯ø“DrpŸ\í¥x§ypÀøä½”à%isüº sKq5àˆ:ýžmñ¿ÓÇôØÉ•çDº¢¼ì€×š6M›eY¹¼®€}«7<°×üQ"'7ŸýÆÈŽöÙmg(Y”À9=ë"ž kúÐÌq÷ÈØCv‚x_¨#_瘣[¹²¾)À+AuØ[ã·•À—?wXÓ’Ín‘\v‹”REé÷Säµ`^+õ)­Ô§t¡9£õe2­¶²¾’íõU Xé–ËÁy¬Û­\O1+½ceß+]t½Ë–kZ¯´+¾_\<«^j}#Åt\-Pë ß©{íÒ´KY6 ì2\ººrµtám‘ Ýa­ºöÈn~:´–«öÚîz2®å=«ÆðJ×\ËBݬæ±öØ_ØVö»\¨•AèRÜzy}Q1Ý¢ôʨ»^ÀZÜZܺyÆZÁk=%¬ô’UÀz¡•–Y n­©i¹â¤K;ºNÙMÝÜ~ù÷¹\$]êÝþÖËøë ÈkôÅôÿ]h•d_Tþ%tEXtdate:create2009-12-31T00:45:49+01:00\Þ”ó%tEXtdate:modify2009-12-31T00:45:49+01:00-ƒ,OtEXtSoftwarewww.inkscape.org›î<IEND®B`‚openxenmanager/images/tree_paused_16.png0000644000175000017500000000405011636446664017063 0ustar rrsrrs‰PNG  IHDROc#"bKGDÿÿÿÿÿÿ X÷Ü pHYsHHFÉk> vpAg\Æ­Ã…IDATHÇEÍiTSw†ñ÷s!IH ¬DDAÑqE™VÄ…ZA—bE‹¢8ÅÁAí(Vp)ê N­”±Ö]Ô.E«•Ó"($Ê–•Ü›ÜùÐsêóñ÷å!ÛvïžÞv.È&½”&î6E_N¾-W\p Æ»»¡°æsn¢ CaÝ›…á!1ƸüñUò“>9òÿ–N³79’{ft¼RW¼:Q³°¬ò©)Žw–ޱqþ “Rw\§üÙÑ–í}+9Œ¨$q:4à€="²QµõYåQ€Ç§š$ô+JÈà.)cÈ~7ªzúÏk½3G»Gùðä¥ÿ ¯÷èĊͲ‡Tévð©8wãm­/ëÁ¶kV¿-5mnz5Pôæ‘zß‘´keyéE¥) -EÍÇ©kÏ??’»‡5Ô)Át²Eõ…¥ZLRë­3M7¸³Žy܆ò s›p4fÿ¥E¼×eó$"œ¨¸ë‚"?ô“ ú8Ý%÷=—Š¿·EZ÷á ‰eÍ…ðã~Ôçd±Z’:¸¡' —¹^Û?­õÌùŸW®~ÑïóÄll‘­M±£š® –j'¡ °}ÝÙ%ÝK'KsWEýäAUó\}×M\j¿ÏªI»Ùo)ƒ‘ªg¾7»CN¢,á¦:´’e&›n †ÉXC¬†_Ô#µ¼ä›ŽèÍ”ŸýCÆmÈ]œºÛq7ÙS¾>N~&!#«Üñk_üÅò«è&ÉQiþ-BÀ¹„Ji—Ì£Ãù5ÁÿVéÜÏß'߯Žwêv®7Ó üùü§9æ/:Qapf ì%„>R†Û­¦ò¹.pæjS({Ävs¸Àš X÷÷³Ÿ[[/j9únÚ©žnô±°?¸zG ‹ÂÓŒÏlc‰Q÷®#¿¼wMîÀó¾ à©ïÕþ"§Ar`,Ì «¿ó.óæQw÷»Šodt%Ü(Üå´¡ý.½„þ˜Wp;¹¹\0)Ú5fïJuãe€©th¤-ÿý¨3Q#²‡½ÝúP{ö{×·™·Ø'µálÉöªÍ@õkô/ô^!~•FH—òªtTà¹Àg’ÊZóç ^ži-àdÒ…r·ï(ü ð’úïœ4ü êö[4@źÍðš ð®JŽJ7ÿáÜ?Zà.òù¥€ …òX¯&N Ðߊ î þK¯ìh_÷qúÉ¥Ï6Q7êoyçÕbw]îÕ*ßyõ% S(‹²ÎµÐs› ¸<ðŒÎ+î¦÷”ÂR±³ÊØY¼Š7’¸÷ó;ÆqÁÀEåÝÓÀ Ÿßǘ°·|=‡Z¡œa¿FžËoy ïò¤³íëS2Ï<÷3Ö‘¸šxAV6ÀÿŠ¿ž8áÞ`‹ERõ8ÒPG½UŽS#^Zä&´{¡†?(ÙÀÛ-øÑ•®@†Ò¢°ò?Ý~Ê‘Ë Ài%Vhë?•̰“ãRVŽËº†ÐgÜüx#Î6J¶N˜ûv_T„ Cï`âŒéע…ÞT(·ŠIä­í¨ËÙÒ–¨_ÐúÕâ›—ï0Äå[E¡¢g䨾,’cý½}»Eù'{)mqêG:éÀ,L«ë0ØNª¨{ÅP<¶tþ¦úŒî¤çº|ÂW Ínû4r«g)ù ñï÷¦¨^yDsŒÙÃÑ{È~žBç]­çøáÂÓt7åj8,8èõ¦S:ÁCi¾pVHŸJB}ê}[|emPt˜$kurS²ÚUEÄ^yž'EéDI… ÉO8Š­/-ÿ6's;ôWJ&ÚÜ?í:¸j;ñ“¦ÉbÕ‰:ŸÁlcÉW&ÂZÇí¢Ûø[ݳ!xíнTÒçK˜'ƒ«f·©\,ްÔÉAIå1"~úÿHè|‰Õ±–%>qE³aAœ†àV8.ëæþè'a6?¾ý`ÕœGs]GGdtÖë³l5$FÒÄÝo7“cšHãba9— ;)dÁåÿ=ù{>1qaÈ"zTXtSoftwarexÚ+//×ËÌË.NN,HÕË/J6ØXSÊ\IEND®B`‚openxenmanager/images/prop_cpu.gif0000644000175000017500000000227311636446664016072 0ustar rrsrrsGIF89a÷26717827937928938938:39:49:4::5:;5;;6:;6;;6:<6;<7<<7<=8<=8==8<>8=>9=>9>>:>>:??=?>;@?=@>:?@;?@;@@<@@BA=AB=AC>BB?CB?CC?DC>CD?CE?DD@DC@DDAEDBFDBFECGE@DFBFFDGFDHFDHGEHGEIGFIGFJHGJHGKIGKJFKKGLKGKLGKMGLMGLNHKIHLIHLJILJJMJJMKKNKIMLINOKONLOMIMPJOQMQPMQQNQPNRQOSQMQRLPSLQSMRSOSRNRTQSPPTRRUSPTURVTQVVSWWTWUUXUVYVVZWWZWQVXQVYSXZUYZTY[UZ[W[ZX[XY\X[\YX\ZX\[[]Z\^[X\\Y\\Y]\Z^^Z_^[_^\_^]aa^ba_ba]ac^bb_cb_db`cbadcaedbfecfe`efbgfcgffgedgfdhgehgijgfihfjigjigkjhkjhlkilkjmljnmmollonoqomqporqqrpptrptspusqutrutswvuwtuxvuyxvyxwzyw{yx|yx|zz}{z}|{~||}}~|€|}€~x…€‚€ƒƒ€ƒ‚…ƒƒ†„ƒ†…„‡…†‰‡ŠŒ‹ŠŒ’ŒŽ‘Ž’Œ’“ƒ‹’„Œ’†”†Ž•‡Ž•‡–Œ”œ•œ–ž”¥–¤•ž¦™£«Ÿ¦¬ž¦­¦®ž§®¡£¡ ¨¯¬¯­¢©°¡ª±£ª±¢«³£¬³§¯µ¦¯¶§°·¨°¶©±¸©±¹ª²¹ª³º«³ºª³»«´º¬µ¼!ùï,þß H° Áƒà „ö®ZÃwÖÞiCHp—§T²BÉâ$ë.G»ìx¡X,YÉdÉ\ÉJv¬å±bw¶dÇŽœ4gÎ”Ñ ÆË–*U¶ 3Øl ”$HáaÆŒ§'N¨xfp\¤>”ò¸qã)Œ¨ž˜3m Ò$7 Øáú¤Š‹¨'d0,ˆ®V?²Šuúåȉ¨¤ -¨¬ˆ®Â¶ëÖÉš Nt8±ì ®7¥r4éËo=\ qPÛš3’¨Ü¬X*‹‰ô\(Âí ‹§7ì*iL“.¹pá`¸12XÀ(d+ŸY‰>t¸@á‚qáÊ8„ &8x¦™¯~p›¨2^ý:Õ*(PB(Èh5©P!>“@Á€ó„Žl°Â,tÉ' t Ð7XAÄ]pB*˜xâ (@` Dâ„D°À"’˜…;10ß„ ä¨ã%B<‘ˆ ‚láÇ‚Œ¡GzŒá‚IôŽB is‹6ÎTÃL”\R;openxenmanager/images/masterpassword.gif0000644000175000017500000001170211636446664017316 0ustar rrsrrs‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<?IDATxœå›{\Åu‡¿î¾yììC•X½½eHŠ+ÁˆgÙȤòì” ¶Ë%§pà8Á¥‚2Á)')S6~¤pbI1²W¨„‰´R„x.b%íêµ’VÒ®V»;;sçÞîÎwîhv%!´»Ø®Ê©:uïܹÓ}ίÏéÓçt°Öòÿ™äïZ€ß59ïWÛ6mJ‹Ååóù¢(šEQ“ÖzºÖ­õ!­õa`·ëº-RÊõŸýìg‹ï—,ïFb¼]`ãÆóµÖ«‹ÅâÇúúúzêêêH¥Rd³Y”RDQD©T¢··—ÐÝÝÝçûþó555ÜvÛmíã*ÐyhÜØ±cGæäÉ“…aø'RÊš‰'’ËåPJá8±¡…a@6›%›Íâºn;vÐÖÖ6X__ÿ_555¹råÊ¡qì<4.lذaž”r}†sr¹µµµœ8q‚ƒ²oß>úûûñ<c ýýýchjjâ’K.áÒK/eîܹ ²qãF¤”{¥”[¹råî1 w37n¼ø©Özbmm-žçÑÕÕÅîÝ»I§Ó¤R)2™ žç!„ˆ;‚B¡Ààà }}}ìß¿Ÿúúzn½õV>øÁÒÚÚJooo뺟ºá†6Œƒžç¤1°~ýú9žç½¢µ®Íårcxíµ×¨­­%N#„ÀZ‹1­5QaŒAã8(¥p]€Ã‡³sçN–/_ξð^ýuú£(ºâšk®Ù;^ ¤Q°iÓ¦TEûŒ1SÒé4ZkÞ~ûm&Ož @E(¥´µöEcÌöR©´ØV*•„µv9°<Š¢åJ©µµµÊq‚  µµ•3fðå/™mÛ¶áºnw±Xl¾úê«ß—(1jZZZZ¤”×»®‹‚]»vÑÐЀ1ÏóÞ‰¢èö[n¹eÛ»µóíoû€çr¹ùétc ­­­\vÙe|æ3ŸaóæÍÔÖÖnX¾|ù £ô<4ª…ÐsÏ=7ÏZ»bîèè ¶¶–0 1ÆØt:ýÍS§N-9ŸòwÝu×¶ÞÞÞÅ'Ožüfoo¯µÖ²dÉ^}õU^yåfΜ‰ã8+6oÞ_ýl6ûË›nºéͱ tçw¾¥µ^W*•H¥RtuuQWWG¡P@qûXÛI @Í©T €|>Öš0 Éår¿¬µV±)Š"\×ÅZˉ'ba¥l>ªé‚(•J)%RJ‚ ¨,nÊq~\H±MkÜÓÓÓ“ä™ñê#¡ @kí&KÚ ÃÇqlGGÇëã%T©Tz]¡Ã0$‰4®ë"¥tÇ«„F¥µ)%Zk¬µ8ŽSèïïÆK¨þþþ@)u*Š"¤” ¬µã^ÀSƒÖZ<ÏC)5^òT¨:O(•JãÞ~Bc RÊñ¯¬I)+uˆÁ~ôÑGBB<"„ÔÇ €ïûï›xÞéuÏO<Á“O> 0¸X'„¨k?¿·P À‘#Gå«éãÀKBˆicéç÷Ú’9à…^ »»»ò].—céÒ¥är¹%@«bÉhû3žç½¯ÏçÙ°aXQh÷âÅ‹ï¸ñÆvÏ=÷ØiÓ¦M#¶„›FÓϘ$w§RØOZ½zuÅÚÚÚ†>póK/½ôï=ôЧ†††î¹ÿþû£+¯¼²xVñWÚטR¾¯Q §§‡ÖÖÖ‘_¥’›‡~ø[íííºjÕª¡Ï}îsJñ¨⟄ïY ß[”RlÙ²eäè<)„˜˜|øÖ·¾õó-[¶|ø#ùȉûï¿ÏóþøO!Ä{Z6Ÿ·$öƒ5·ççÌžãe³Çšˆ§Ÿß:¼âeñ—ÌfNóT(®záìtF·§ìÙw„í¯wðë-¼ùæ™ æ¥ gñ{oÞÞÝYúêšµY€›o¾yÒõ×_¿E1÷ë_ÿ:}}};€[k½›~ç`Í=7ÙL:MMM ³.žÇ”)MAÀ©“ÇÌ1Tˆ"±ßsɤ}<÷Ì7)BÆhXc1gé·F ‚RÈîCyýá3œ:uê¬r-œÓȵW.âÑoª@¼lÙ2÷ŸøÄ¯›››¯úÚ׾Ƒ#GvµÖæÇ @&“&“N‘I§©­k ±iu¦JgÁFDÅSDÅ>¬ÑgmÇZKÒ•TöÎH*”_Ë7ÐÂ[oµ•³¾»xÑt^m;xFC<ðÀSÍÍÍ·|å+_¡··÷À[kÏ*بH—¯¾ïãyél5õ‘©¿/U‹TN¬¥Ñ`¢£K˜(Η¤ã#•TBy $FktR,œbÏž]|ï'/ÐÓÓÃÖ­[éïï¯$`Iå¹Lw[kÿy¤Ü<òÈæ\.÷G÷Þ{/ù|þ1kíc •ÊVà‘N§ñ|Ïóð}G–C¢ëây>Žë£œ~ºÇ«Áñ3 T¼Al8DX¤ä KÆ”à ­ië<…ÖšB¡@ww7[·neêÔ©„aHWWI½0À­ÖÚgªå^±b…³råÊ7‚ XøàƒÁßXkÿa¤~ï=\Hƒ”! B”}ÚÚÓ,Êl-FǬµ&,„ÅS„C' Žô¦Ø‚`àCùSƒQdÐÚF‚Ð †J¢RoTJQWWÇÂ… 9xð Ùl–éÓ§W¯=$ð!ÄŠjy_|ñÅhíÚµJ¥R]wÜqÀ!Ä'G @B±ÿ‚¥J)„”‘0H³ã®«ð<ÏsÊÛb×U¸ŽÄU¥N(-¥¸àZ>K€RŠÉ“'ÓØØÈîÝ»Éd2̘1£„ðK!Ä_T˺uëÖþ 6üáìÙ³O.[¶L?B\5& ¶9cN3å^Jã¨Ó¬’{ÇqRâyŽ#QJTXR )$² ¨”²bÆ\×eÖ¬YH)+¯#@ðˆ-áïªe]¿~ý¾—_~ùÆë®»nh„ >ðßBˆ…£À F–jb6k Æ‚ÐÆTsH„å RM ´6ŽÛ‰€•p(âû~eC5ñ÷T*ÅôéÓ‰¢¨ÂÌ™3«AÀ7„ÿ*„¨ÄâuëÖmokkûÔµ×^º®Û¬BLñ<˜¬8ƒ%‰EÊjÈòHK)‹”ªÌñh‹²oYj|߯(Ÿ\¥”466’Íf‰¢ˆ½{÷’N§‰ó¡aôù²’µÉƒgŸ}v]GGÇþï4Ï !²£r!l9®gm-H¬-‹Űwu9œÅœX†ã£3ÍÓrX«+ T»‚ã8Ó×ZÓÙÙI:fúôé#żxY1#yÐÞÞ~0Ÿ¯¬‰–z/%gŒ¾Gžžäššš˜ð …ï¼ó—^riådˆµ–C‡öóó'¿K÷·òt>!¤À¾÷ê÷øPÇ!1VªZáê¼ZqÊs€P54]Ô|Çé‘ßõö._¶¥T•ÙwñüS?àø¡]8ʉ°rÚh;ö ™÷ @,“¨âá‹›‘,m¼Âsª¢BÏ)˜:õ"èìèdáÂ…”J%ÚÚÚ¸ì²Ëp§¢ü¡ƒûyî©ÇèézÏsP‰µ9‰À`)éߢÄ•ž‘i®¬Äý‘¬ÊÅ$Wïˆp', X,ÒÙÑÉ‚… K!o¼ùK/A)UI”ØÏók¿Oo×®¸’亨r‘E*‰Ñ†ÈX(ý¨ÿ˜×ïf}yKPš„E2©±iöìÙÃüù󉢈7ÞxƒË—\Ž”²â>ìcý3ߣïh;é”ëy¤Rñ^ƒt$`‰"M†„æ·@<Ûƒ)›tÂÚÄÕ¥öLƒÐ†Á’ENX@q0Ïܹ³* ±{Ïn,X€Öš×^{Å‹Oû¼‚ý{Yÿô÷é;º‡”ïã§<Ò©¸ ¯œxî7DADd,J½{â3.hc‰´! %¥Q*DH…q>•¿WÊ)×ÿB¤xSñŠóçϧP,°g÷Ì_€1†;w²dÉi³B°¯s/Ï=õ}G÷àùñ¨gÒ²™4¾çaX AVD Ee[î}ÀiC¨5*ŠP¡BÈ8w7–²%X”£q´ƒ 5&ÓD.7™¹sç»Ûw3oÞ<,–Wþ÷.¿|¸Ùïß·—_<õúuJù¤3å˜LŠ´ï¡”ƒ±š0(B$kö»Kã €¶hmˆ"M(5RF $¶–HÇ'= „bæ¼Kh¾¸™°ÒÞÞÎüùó±X¶µn¦¼µ–û;yîgߥÿø>R©4é´O:“!“õIy~<ñI‰ ‹¶¯ø[˜LÙÄe¤2B‰-‡B­-‘c#].z(œl³fÍÂhC{{;sçÎEHAëÖV®¸âŠXì$ÔØÇú§¿OáÔ!2Ù éTŠTÊ'•òñ=å8)16žõu¤ã«ŽA?¼3 !”"­%Š QdCM)Œ(•"ŠÅb±D¡X¢P* YöÑ?GJÉ®]»˜;w.Žë°}ÛöÊÈ' ¥Cö±ñçÿ†.§&WK®&G&“%•JãzR:X—ÏÃrßadÐQ FRZBÌB4 !ê„é 9çØ,×Ô3€?‚Sç{väÈ‘éß)ß',Ë¿5Um¨²D§IEND®B`‚openxenmanager/images/usagebar_3.png0000644000175000017500000000066411636446664016277 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<–PLTEnß®üuvvvÄÄÄ‚‚‚ÙÙÙ¾¾¾qqqããã~~~ËËËÜÜÜçççzzzéééëëëßßßsssn× mèménØ!C~B‚mè®üvB†mñ A–¯ò}¯ê…A‹ ¯øyA‘mßnà¯æ‡n×!¯üv¯í¯üumò B~ÅÅÅ¿¿¿êêêpppÓÓÓÌÌÌÿÿÿ '2tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ P˜ïvIDATxÚ¬ÑI‚@ ÑߊŠ¢ *8¢‚íÐÞÿr´Ëì² 6ï…ß ádšÂ˜·$°vmm¬Õ±õÝ$a÷/Òƒk–Òô,Y9·pn®%Àý%}|ãì“<Éë‹dûõM—z@´#¢§„yÆÌ=fx/À%L‚h!‘°IEND®B`‚openxenmanager/images/prop_networksettings.png0000644000175000017500000000343211636446664020572 0ustar rrsrrs‰PNG  IHDRàw=øáIDATH‰}ÕypUgÇñï{Î=÷œ»æ&7+Ù¡laK! …Ö¨ÔnJiK´‹ÚA¥XuÒ±c§­]•ÊØ)¥‹ÃLS*Æ@±¡ÐT–¥¹Ù—{ss÷sϹ÷ø‡##­ðüõþó¾Ÿyϼï#,ËâF%nûSµæP¿P˜«Õç8íå€5HŒ‡Rí©¤~Ì:±¹÷†û¯¨w¼^çÔ”g—ÍÎ]¿rn¾ZYìÆëˆ$MúFâ´]ê§/F¦ã»­÷wß8y²µ8O,ùå/ötÍn]S•¯¾úÅÆRÇêùù€Àë²a“% 3Kp]à{;¿;ÒzªÍµëñû7mªÄïUÉZÐz!ˆ™±H¤3 †RŒLéŒM§©*r2§ÜÃ{äåhÜÙ0ƒWß¾ò»º¦×ü߈,êw¼ÓqO½>ÿ;[>ÇÓè £xQ Õ.S–ï –ÊÔ3,¯õqêBId³Y ÓbóÚršÛGyï£Ð¹ÃO-_"uÍ Ž m<ß;5ï)…-»÷óЭy`&˜Œ udYâÎ%…,©ñÑÑ!‹ 3錄tÞ¿b$¤Órn¤¾ùüÔ築¨kTw¾Ñ:tŸM–‘mŸˆyüp_ [W¹Q¬Š E>•ªb…>•îÑ$}ºG L¦ØÐPÂkͶ  Ç9Ü2xïÇCI€ `kÈÛÞ=ÝèuÚð¹U\NbÏî?É\Å`ÂÉù@Œ`Ì$–ʘLaf,ҦŪxáH#S:ºaáq¨´_ 7ÿ8ì™WêHJgûîXÒ(Ñì6ò½ Y3CU¾JÃÚõ¼õ÷NÊÝIºã:5Æùþy„Ä÷7Íä7Í¢z†ŸF}u’,ˆÄÓþöÞ¤v5"§]–ìvY’çj¼õ£æWçòÖ™qúäüô§yâv/3ýšÝÆ= $¸­ŒÃ§ÇI¤-ü9*y;ýãId’,Û¢†$]¼^Íp»5}q­á`’Í/up¼3„Ç¡ ›¢l%/>ÃÝ‹e6kÿ¢fÏ7©N^!ð8dI0I£ëd!(-ö¤ ræUÀïÓ¢Uåy½—â O$‰Ä dI ©6„,“ã¶ã¸i©æÓÔ÷ KáyçÏdØl2B„e‘ÍdðyTÊŠsn‡š¼ ”¨‘ª2ß»–MA–!cfɘY$I Úe* 4–%.þY—Þ|ƒÙþ{ë‡,>õ²,‘ÉZº‰eYT•ûX\›÷Þms]¹©©‰J¦+⟌;b‘ª"c³I¨ŠŒËi箆fΛÉ?úBävÑüª¬evßÈ=£Ï?‡´ì¦¦ÄÁ¬Úr4_Ñ7^&\ó’vìûèP÷¨uw*Es(l[W‰Ç©ÐÚ%•‘PUe¬Ÿ²—w°HspËWà‘}œÐóåÇ .Û‚aÆüzÛÂÍŸù*ò=Yó¥o=Ú¶¦FÎÍiŽw†‘ívŒ¬„¦)ØI€– ³t÷íÔTÏcÙ}ÓùÛçù°§ Û×·&9¡Ì‰^Øøï™rSSÓ¤œF‘U¾Ñþþ´ÖÜå)C‰D¢¤“ ìv ŸSÂ''(ÍÓ/C…eØì%S¿}'jßE›ßUZvÏúêcG?<ýâ랊Y±± ­·ë“Àœ³nª©­t—ø5ê<“X±¡a†FG°¬,®Ú¥†.’ßÞBdjš…Ûv"wwèè]b^:,ÙôXÛ5 ×BAéÆj²éå¦ßW˜W|džƕ‹–ÖÎwÚu$<‰$46D"ÇB&4ŽÖ½½×¾:Tón^IÃ×¶q~ï3¦²µwU6Ô~MDÍ. ˜%„4'7Ï[RP”멪žáŸ]WYVQ‘ŸïÒdWïųZÇû'ìž+fp|" M˜HÁ…·ÆcÞ¢ù+X¾ñ^:þ ù&Û‰n9½Ëö¿ÃÁê~.%ªŸ¼$$1%Û¤J§S¡ØlþTRJ$ÓªÛ›k«œÛ`…#utx²Ø2üVV·½Ï´Œ ±d]{Ë ¡§YöÔ3ìzíÅ–Ÿ·þ³çºC¿¨ñy¥´¢Ð]^Q”S^Qà-¯(t•øÃ0˜Hô%Úš‰+moêÑÑ™X™²z!nù¶Ó{sÏÂEGÒvj§e‰ëŸ®çŽDaqž0 “áÁI«éî @á*€RJ¶»<3ÿjÇzõdÀ¿µŸr6EIEND®B`‚openxenmanager/images/centos.png0000644000175000017500000000134011636446664015547 0ustar rrsrrs‰PNG  IHDRóÿasRGB®Îé pHYs  šœ…IDAT8Ë“KHTqÆ÷þ¯wÒ1KsM©Ì" 5”ÞîLµzÓ¶ 0ÐMѶlZ×ÞUEAÛŠ” ˆ™éBOI°5“fš3ÖÜqwîûoS–”Õg÷ç|‡£H)™MÒÉ ¡hztv“”,7Ÿ2Ì;d&zDºvƘͧü˜À)ZFÊŠH—rO=é½!}ìdK(xwQÑ´±ûˇ$BS©lðE…®EŸ3?ræÊÀêЊêC˜Ö;ÆÒOØ9¯ƒÂÃ:úN½dÑ–åÌYàåmïGŸî®¨«< ýºN‹ÿ0šVWTñyê¢*¨º ¦}NÞfå¡5¸Ž;Ý3 ðhóÏ<¥´Ä‡¯¬™ÿa yiKÚ _c ò_s”ùʧ*€9–2oÝ % CŒf1šîU£º|UMõØ9‹Ï#Ä#¯>LïµþP"‘6”|:g\ ^ˆø[k¨ V¿ŒK©î£q_sÀ*1®_JOX¨ªÀÃÁ÷ ÇÜê;Ð<ås¢mG7GŸRž»¨B¡èž½ d†Ï¶Ô§Øo¤ÈÅn€ª£*EL;Ⱥ5 ¯×¾BìÕX8¸ã|@×ǃL&³lÝí²ÌŸ¤f2 2C= r›"‘…Kƒ3Bœ»ÀîìÚp]ÉÔ”EôÞ ­>½ŸÈÅoãYz²•H*jç‡g„·‹\¾ôÇ.rÿþâI„PªÀÍŽbÅo¢õæªmñ TlXßÈø¸IW×vŒÀrªª½½Q»…Ò–“8fá_‹žÿÂDöqøò‹vyñY›1ûÃÿü…?iØ|v]›ÅóƒÁÙ<ü¾Æ“`pƒ¨ÀIEND®B`‚openxenmanager/images/paypal-donate.gif0000644000175000017500000000265711636446664017007 0ustar rrsrrsGIF89a>Ä2WApÁÍÚNn’ÑÕÚˆ ¸´ÄÓÈÓÞ×áé!XöøúDe‹çìð¤·Ê°Äãñ÷pŒ©áçíVv˜|–±çõúîñôhƒ¢$Hs»»»ééé4f"6M”§½ê÷üÿÿÿ!ù,>ÿ ×pdižhª®,YXCáÍtmßx®ï6P|À pH,ÈáÅò¹=žPžÝ= ØgöÉ,‰‚€f¬ ,I"ãB&£@À ák›\ €3ˆ< wˆ’3 3z_C LC … u1ª ªuD›§ šL y{L5  Yc¬…@ @ÓÎXf½¿C h?BòñĽ 4$€àmÀc|p ˆ'`48\P°p Å…$\àîÀÀ4,ð– ™T>ÿ(0cac7P„Aãá²$ðaàƒ\&àCƒâ©`鄞?1üàáº~( àQ€ötû a§ÄÖr@€H ¡A`A0PS]E1–96ñĵ»ø©-À… ÊØÜ–ìh€8M»-Jé‹ÏŒ—^8€ÀC<8ˆ3‹¡Âs (jÎ sRFàÀ™ÙÏK5l°€«©©5¬VpÀgƒÑˆí²0¸«eŠ`€FÆ´êÙÂ6j@Ðp ™PœÁz2œhrܦƒà~AW\)8 @Ö[L ;æ”C œ@‚=MH EäA$Å ³dU Tòá‰Â¥Q§˜ŒŠ0væá‰4Ö¨CŠ1æ¨c6Ùèã}}‚DDvD‘@™ä‘D&Y¤‘O&ÑãÐPe•3\‰å–Vv™%Š¡äPIæ™J>9æšMJy•]béÁ•Yrùås‚)¤˜H:™¦™g.)(™}1%ˆžÄCeàè£F*餔Vji°Q¦têé§ †*ꨤ–Ú)´·Áª¬¶êê«°Æ*묮†;
openxenmanager/images/windows.png0000644000175000017500000000143011636446664015746 0ustar rrsrrs‰PNG  IHDRóÿasBIT|dˆ pHYs  ÒÝ~ütEXtCreation Time6/24/09öÁkãtEXtSoftwareAdobe Fireworks CS4²Ó qIDAT8•“;hQ…¿ydîlv“ÌjÜ„ÄGD„¨¨EÀFm{í,´°°±RÄ4ZXZŠ…ˆøDlÔFDD|A ‰èúXDvuÉdg23{êÊ‚Mnyç»çÿ×j6›“Y–ÍE°Œãy^äûþ´U¯×à ‚jµº?aEQä¦iÔj5Èóœ0 QJ0::Šâ HÓ4p•RH){—Ù,Ý„ëY`[${2vìžçõ@yž#„@)…+¥ì½–ç9›ž=¡5â`XXŽÅðB“ƒ/ŽðýåŽÌ§Ö¹öü2f_!¥ÄUJõÇwÁvllaQ2>ÏÒÜ>jS-.}8ÆRfX99È__ tõ$Ö 6 mZƒÿ•rÙ ¬EÍá§þþŽ ¥Ä>y–/+W£”BAµQçcò–®(”Ðõ Vlíy\¥Zkâ8fÏõ§3T‘t‚AnngÜÑ]w‹;ß®1U[Åöê6j• Š¢èOðüSÌpªñ}p}ºö0kF Ì»µè¥3ìÜ‚?elþ.Ù®+cú—x{.EKîjT¡‰’Þ³¹b˜ôf!7à¾m¼Hú§ö¾¯?1³{=¾€ù¯?94Qà¶o@Qm k ÌèNÏ ²  ?Á›sHù[pœ!>6S&æ/ƒ2 i [E–e½Öz5ÆqŒ1­5¶m340@¼ã EûÒ.c9ƒèêNŠV €$I~ljZ­V ”¢R© µFJI·Û¥2¼§w—þøÖšN§C»ÝÆqœÈõ}:I’¹F£±¬ï,„ˆÊåòô/¬Äyç*©nçIEND®B`‚openxenmanager/images/usagebar_9.png0000644000175000017500000000100411636446664016272 0ustar rrsrrs‰PNG  IHDRFs¢yªtEXtSoftwareAdobe ImageReadyqÉe<ÀPLTE…ppppÝë C~mñ ¯üunàn×!¯æ‡A‹ ¯í¯øymè¯ê…A‘B‚B†¯ò}m߯üvA–®üvÞÍìûuÓÓÓÞÁ ÌÌÌìúvéééÝÛÝÝÞÃ!ÅÅÅ¿¿¿ÞÏìë}ÞÁ!íÙ‡„”ìôyíä…tíÞ……… ÝÍÝé …{ìúuÝÛ…Œëëëßß߀€€ÙÙÙvvvmèménØ!n× ®üunßmò B~ÿÿÿ…·^+@tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ{±DŽIDATxÚ¬ÑG @Ñ{ï‚1ÍÞkˆi îWB øƒ¼É]Àƒ_)ÁŒOçœ×ñ¨ã<)5D¡« aPûªºxÞ÷Šêhˆ:ÚiÚK’1ž«ï¿ïó>ï;³ ÑæzfEÕv­È0M€Lc`ŒaÄ0a˜7ÚtZÏ­ñªmKŠŸ¢áHìýÔÙ+üØ΄L~øòo ðYÞüßlOTT OR#^Û$Ó°Ô»mîÉÈô9i†¼nhè‚Æ8ˆ“ëöœÓŸÉæOK3™L½7 ÀSQ±bðá™y]êÀÆ{qÿ‹€‰É³ì¹‰>±ÈøºqÃAfgÀ½téâLºNÈ'xèõÕ›qÎu+Nþv LפǦŒu .ÿÀ¬¹(€ø9;€aê!D¬‘vKö7·CX,ˆ MSÐ5Ɇ¦Bd­,æ”11jrS^‰ÐO£²º{Rª‰w½‹Ñ9> 3Ê,FI é— $Öl "÷Þ©2¦Þw;VUúåXy+bÎ1ãžL˜&Wž3µN" t‘¾ 6#;ÿ%%>‹V#ÌSSØÂyE=¥´]8/ßÁ¿0*æÅ;aæ("(¨zuõVtêãÐòóqª–LE±\Œ+OF„gÜ©õX•±0k2nˆb¼\xçÃGÓód@XÈ¡N©¼ÌTL«ÿ¥ú/õø¾­W:¡[LÄ&‘fÅ_qæÂ§ë7 ±¡I*Ç´ªŸ ¦ºXA÷•yðûåýr_¾ ௩‘ã2¯¸W£@²è»£1àY©ç±qÝ.d—|æV¹¥Ѽe¾È"¥Þ2Ç¢e>Ÿ-" ,ª–@o[(\<ôò{;Ñ£¥ ©éˆÚ z36ÿËË}˜>}†ZPPÍÍÍ•Â"å”w…Ù*ßaÑ]Tj{º»ñCk«¥eÍ ‘HCñz˜ÒR¯c!¯×›xbŠÂHPÆ0I[¶í@KËQE½!S!°Ô¬[”—‹¸øQSS-ç|¤¿_é j÷.e€E;7ÿ†*×mEfÉÇêï³ÅËŒª˜ ‡®šÄ@YÜb^›ŠŠGhÀ„G×#¬%*Æq(YÀÓQW×Ñ€*Å ‘(ÑÍ¿÷ËÔtÖw{’©]Ó­{’4VΦ!7¥kjÀMÞ>Ù†cgÿP¨­5í±‡UŽãbŽßŒœHv{ðÚz†)Òñfa4b#v;Ñ€ŽÚ}ÑÖÚ&+PG´Ä$ºî”Ü.ZÔ­»¨»ÝCÞ'‘{n*w¦uaMõdx\b¤8Bü»º^¹£ãš7!+'GîÜ*Þœb`“êLNvAŠ'c“’b:1àQ$ÍÍ[„©é°¶z.\ZÆè(" <ÿ~­<¬j;ä^fšuÔ~Qç=.6|[€úã~ux‰M1«ŒC» …Ùshþ ý¬ûÚhlü B}VÚ„K—¢ž;×%½Âø–¢{¸J.1³ò‡vDž£[P”ó\žJ ùðÉ/æm:ÎB‡[~Aõ`ÒĉJþ6o4uJ­8„úÖ jìyPLî‚æZ.VbVÖ Ð\ŸÏv-ÇöÀÌŽÓ@zógÈÊΡ£øˆ¡E­naTŒÊÈãÁGÂÎHL\ÉUNŒ¸¼Óîº[Œ‹é‰1N ,' ¬­•G¨§ORœ5±ñ PúqzF=/‡+ `ÍÔÑÐÚ‚¡ì—åß®dcwõ^ r÷©(Ä& ’‹Üµ›H¬fPkÕ›x\EqdØ1pê °g/ö×­ÄaÛ¡qô§œª€&¥ ÀGžó¢4™×Å9Ocå_Ç10L)5ÿY•A§Ú¦‹P·²jÝ¥¸aÀþa¯ö^®g7gäÙO·n‚]²ªZÁâw:QtÞ@†þ–Ø/c*"ÑÃSÑåΕ œø•Œ7@ï]XO[U‡ÅºaÇ->—¦PóÅt#Í]¸ 5Kûö 3¸ÄÎSo·…Û€uã"𾿷4¡“ƒÐCÆŸ¤ñËóD;ÈÑÒ¨ßf9zVœS-Ñãÿ@U)Öï¾Xã¢ý)ÀBÿý[ÿ¾ïIEND®B`‚openxenmanager/images/poweron.png0000644000175000017500000000426511636446664015756 0ustar rrsrrs‰PNG  IHDR szzô|IDATX…í—{Œ\uÇ¿¿{÷=wÞ3;;ÛÝí²lÛ­”Z©´@¬EÀZ Ñ ±¨%D!Hßñ¤1©‰@"*˜ØRBÑV ´…–}´ûdwvç¹ó¾sç>þ±€BbˆÆ“üþúåœï''ç‘Ccø û@Õÿð~Hßoô-×ÜußžâÜ/÷”2—ßøÂ/¸¾‡Âï‹€1öž_àì»/{ècûÇ{j”±‡09ûŽ+ßO,ú¯é’ÁTOh ]mØ¥‘›Ž@X6;ª@®Ìàû Œç EºÞð‰už¹LRj~nlÒsÊwÿNz×wWmüø¹_üÁ;¶v-‰ö0ßó®¸õèïvß³ëê ÷ÔÛ€c1ø àE@Æ–rÑÏëŸþÒç~vû¯_ØN©(ÎÎÌÌ\x廟ß{ß½FelôítN©’º3¶vËïw}åö¯¾xÇ%7(‘DO±ÎÃôE~pͲm\Wª‡éjŒ@À@|€ibX=}Ź+ÏÙtµá)R¡Á“@Gß'®½åæ/ï|îÈꋾ³“ðݧÔÉ[2 ößµú¼Ï|êÁUk—®\tÜ<(—kpLÃMuè BKðTPà1Â}Ý¡ÄÈTõ6‹… ˆÌeĨ¶aÇm_ÕN¿l³Yy•]>@:ý†åk>ùͽzg:ubÚ†ÿ÷Ü^Çì‘¿À ÷aË¥æc!-ÕÝÉËT®b|¤‚5ƒ:JM suý –J%CØ»{ ÎkO¢gÕ™Hž¶<ñÀqáþug®½fÏ¥ëºMæüÝ“op‘+„Á­_¿Ç¥rjn.Æ^QŸ›ÄÌþGÛ‚|N?@x^¤$ôrÕRN>‘Ew­†¡×š8<íxœâ) ’"£6;†±“Q:ïrHÑ4Ë„çú ‚ÔÝÁgïá7]äêRˆ/_»ÙÑÓçÏÌL‚ãßú£Žîà’ Þ7ÌìÄsG]«ÙhÛ®hÕì¸p¢>y5ŒÅ¹‰á^äùCdmå¡g  œ‹F¾ðzÑØ’ë7u¶ôcöQˆõ­Ø\?†Vv € Pð$ Ûl²P§!Œv(¯.ð†<*P¿Ñ%6Õ‰½Ñ´˜ìqšâ[¤7$PVgæèŸÓÔœ5;ZKêž¼Â7[z€o¡å•àÙ`;¨çUÄÓ©ÍöQºü Ü·wÚù2`VA„ïàÓ *¦m¡šP¡œ]ó=n p2KøêTïd4 Ð^QÉ–õÅ…ù)¬”¼YÊù™:žqò ×›F…_oµ­^¿Õ˜86Ç…'êi"oãh,–R¢²íÊ‹pÛOž€Y„G8ä-]3™ñƒ‡xOÉ0[œuLw– ¬Jˆï©B{_@l¦dUæBŠ]Ìs8.PL;–‘õmc˜8Í8Õn¾cUªÑ0{™ë<àC¥á ®‘)4LÐTTá£bð9Àg@(ŽlÙFªçœóSTšFõåGO>õã¹7:æ’ë~4 Ç*RâxææZñÝÚ¾{¿á0,,Ùxs^Šn¸Ôf½ÜæüëÓªmZ^( ž£jg„'[®¿?Ø¿bÍýCÞ+*µ˜:1…Ú¢ÃU=ñ:46­“Â#A>»7ªûà vñçßÞîÿã ÙvãNζtIÿU/yqÉ oËÖÜ^£”j5À̓ æ…‘X}Îê5ÍŽ—‹YÁèJ¼:65§4ϰ ©:âÉ z;ô'8¤#©0C2Ä×Â* H”ž€ãlC£ÜBG¸…ï_›Æ³|•WFàªÇÁóhk–/ÏpoýážY/zÈdu䥰„ ,OC¡j ·XE½i±]pŒA¦€*éAD²e™¼‰Ù&FÆK8>4ñc#(5kX68€£Ï¼„z¾¼¿ò4hã1ÂsÇßU¦à 9‡P© °ø ¤ŽM»bw‚‹éBa®ŠÑœ€l‹`8ç£7 ¬éJppØÃ‰ŒÂ"P©q¨¹ üX³ðÀÞ¶m³‘Û$™ú†ˆ°ãºøý~"Ñ>ßQjjNPR¥Ê&öcÿyŒO{Íü{Yÿ/Ö¦p×IEND®B`‚openxenmanager/images/confidentiality2.png0000644000175000017500000000145311636446664017530 0ustar rrsrrs‰PNG  IHDRóÿatEXtSoftwareAdobe ImageReadyqÉe<ÍIDATxÚŒSKOQ=÷ÎÌm)D¬’ U Ô `! + F7,$,Œ±‘•!ìdã‚ÄÇ0„£Æ˜øh´PT„Ä ±­¤€†V ÈÓyøÝ)(aå$'3¹÷;çž{¾ùØÝ[8ðt.B„šÝµ4a‚0Bˆî/V÷}û }>_e¤6PW^í?ɽGŽ:«¿~6/e¾SÉĵ•••aZ"däžnkÝ#ß Ÿ÷\ê¼\ê?VÁÊ6¿@¾5óe‡+Quºê›àÞ®‡È† ‹“­T2¹–=0Lû§Q.ô“µ(ÆùõóOo›V¨GIEND®B`‚openxenmanager/images/redhat.png0000644000175000017500000000161511636446664015530 0ustar rrsrrs‰PNG  IHDRóÿabKGDùC» pHYs  šœ-IDAT8ËM“ÏO›eÇ?Ïóômû¾PŠàP¶± ˜aËâÀƒ[œ^–yY¸HLŽ´Z£-ß· B¡f'9ñMsÃÏ:íö‹xÜÝ{¾˜ŸŸ'«b±¸P«×¿öçû­Vï|PŽ•Ò™hÔñ,+ ß^¾jý‰F\uzzŠïûï×jßÔêõOûÛíÈGZÓbôZsèºÑS×= ¤8¬T*õÖÈ;=—Nã «Ùü<åyV˜â úAÄAô‚ˆ(A :A0„Ö)årƒ`2Új}õ™çM :bDˆý@P@øt=L/<´ÞSÀíÀ÷“0pDèY`†»k‡±×Qµö:Zï‡Ï¼ÔÚúˆÁ)¹ëû¼œvº+F8€žK­¤Â‚:½vÒâ"ÇW¯‚R\݈ì $ÐòÀ€ÅãñâÄÄÄùÐÐÐè›W®0ñð!¡û÷ÙZ]¥¾½MÒ÷„ïSn·Ùñ}ý3jÀ?¡………üÒÒÒáÌÌÌôÙÙ™2M“wïÜapr’ç™ Úi`(E½jë§Ož°[(àƒ ä€ãP_,þW__üÓ´>¸1w#%•Ô†a0;7'¦¦§9::"rR(ÀÆãaC4ê—¶mÿäyÞ‰Ú±<~ô¸bšf¿iYS¦iF¥”!PJÑÛÛ‹çºd2ÆRIîÝûÄ–B®çóùg®ë–ÔÊÊ é_ÒÃÃ#y%¥Ôð¶”2H­µ…B¶m\×u“ɱ?ÏΪë;;;«¹\.´À­›·øn}íÜ…³ÍfóÇq^5ŽmÛÍR©\-—Ëùrùïôæææ£µµ§ßomoåÖoü?–e…ço/Œ$‰q«Çr]WV«Õj.wô2›ÍµÖÝð/w)~ᘰ`IEND®B`‚openxenmanager/images/prop_homeserver.gif0000644000175000017500000000235611636446664017464 0ustar rrsrrsGIF89a÷--/..1556779@?CGFHLJLNOPPOQRQSVWXWY[Z\]^_a^`caaecdjghkklnklqmpqqsw{uwqsy}wxxy}{|B¦E©LªO¬T³X·Z¹_»e¾h¿z{‚hÀlÃzÏ{Ð€Ó ‚Ô…ÜŒÞ‡ÖŒØ ‘Ý6¢×<¢×8¤Ù<ªÜŽá ™ä›åŸè$˜à.Ÿä'Ÿè0œà#¥ç/¡ç% é(¡ê-©ë4¢å:¨ç2£è3­ë>¶ðVªÙ_®Üd²Ük´Ýx¼ßA¬éG°êMµì]±àR¶êZ¼îE¸ðIºôP¾õd·ân·âk¼ås¹àx¼âSÀõ]ÃðhÄï~ÀãaÅòaÊ÷mÍöhÎùtÈñ{ËòlÐûrÔû{Õü|Øü‚ƒ†ƒ†ˆŠŒŽŒ‘Œ‘“’“•“”™™™“¥° ¢¥¥¦ª¦©®©«®§ª°­®°¯²µ³°´¾·´²³¹¸»»ª¼Ë½¾Â¹ÃÄ‚Ãä‰Ãá€ÃéƒËìŠÓõ€ØúˆÞþ›ÖòÙøšÜù¢×ò£Ùò‘âþŸàû©ãþ©èþ°ëÿÂÄÆÎÆÁÆÆÉÊËÍÔÈÁÍÏÑÏÑÓÓÔÖÕ֨רÙÚÛÜÓÜàÝÞàßàâããäæçèçèèëìíÿöíïïðïðñóôôÿ÷ôÿúõøöøýýý!ùª,þU HPU +'’DÁ‡=Ze„ªL|à|€ø0‘£3gAù Sž*jT 0`x¤ê eÊ•`þ¤I³dŠT4mBL„hK–4xô¤QR$ˆ&J)U%:¤å¨œ9sè¼9rÆ¡ š‚…M5mÚ¬‘¤GF‹) dDÉ?zÖ¸q³¦’$<1X´@QИ Iî²ÕË×Ò¤I’f°¨AXà.;žÄ‘$©Òž4kÖ¤ÁTé’i‚Itr#IœÇ•bcÒÄI&Ó˜0ebÒBµÀ ±ƒ_*»’¦L™4)ßPÐô¥Ûœ&0 ÀOÊ;yòTòïJÌ8pÀ@‰PÛÓs D9mNŸÐP à@šQž>}jý@ ¸](j,Ð@ ¬A ¡4èŸ@;¤·)n0 Á¼QJ(¤ŒB )ªÄ~ –òP`@©”BJ)¥œâ ŽÒ`)qHPÁÈÑ"*@¦2ã‡.–’ dÁw¤âä“3žbÊ”¦œ‚‡h`AOBI…€dFerÅU~·Í÷ý§´6G"‘Û–]!t¨ËJ©;A܈FݶoúB€•%þ›àæÍ[O)¥^ÇãÏ·µe²©T ˲>磵¦V«R*•ËËË5â·»ŸøÒ;ÿ—`zú~bn~îWÑhôx_ñD‚0 uH` hÝŠ`#‘¶MÄŠ ŸÉÉIæ šΎÎãƒÛÆÿ‡àþýmcccgRéÔ¾ÞÞ^ê&F‡F„‘Ÿ‰ƒA@‹¹”ËËÜ+ômî;´sçÀ?W]—–ĵk×þÔhÔöõmÅó+Ì`„@ˆ¢°ÝÊ ­Ä "ÑËåeæóù{vïݳqcwÀ2†étúÍu뻩V*xžò}ü@ø_)Tàû _©Õ])…ï)ÏÃSF×Q.•Rç/ UÎ;÷€=?7÷B,£R©*…‘)@J)h•A˜ÿ”bŒÁƒ6`„¦éy$S)”RÇ€7쎎ŽL­ZC‡¥RH¤-‘R"„…´ÒXaZÀ­Ê´€…ACˆ @Ù6A¨H¥R©G=°ó ùùÅ¥ERé : °,Û¶R"¥…T)$B>B_áÐm4Z´ÑFÅaa~žb±X|ä'¥‘ =™œœÀSžï£‚(‚ 0Š@ኩ•³ A¢5 ,!X»6³*™N§;×wç@¥Z%Ìs]\7‚í8D£ÑHŒ¨ãà81'F4æâDb8Žƒëº$×$‰ÚÓSS >öñD¢}µDµZuñò•+ìØÉýé{äó³lܸÇb´AHä£f?êX–…‚¹™&§&Ùûå½|zý:ÅB¡¼JÐôü`[ÿΜ=ËÓ{÷Q­•x09N4‘$ÛÞN*•ÂDZ,kEk¾P­VXœ_dq)’t*ÃÍÛwèìÊâÆÜp•`íÚµ]›z{ÙÔ“ãâ¥ðšŠÁÛØ>¸zµÎÑQ,[’H$°„¤ÞlP¯7èè줫½ƒ‡•2ÃÃòI÷/¨œù¼?4ÔnLKé¶Ñaexø»wí¢'·Ž‹ÃñÞÙ¿1Ÿ_àÈá#¼ôò~–Êe*Ë CM<§³³™8;tžKÃÃLîø Î^—¥ñ_ÿ¥áø`µ¾:*>òó¿þúOO¿{–ƒH¸äóy>üècªõ¹õÝ8Nœj£Ž- êºH æf󀿂ó#RÏe©å2;QÆ;0ÒdêÔv`Œüëv&ÓF.×ÅŸOÿ•f½É¡CÏñÚ‰ÌÍÍ1;;Ky¹Dº;B:•bßî=¼6´¶ïäh.6VUîkCôé$›¸m¦NmâàÁƒoîë?sâ?ì½ñ ￞»ìöY¾ýÍclíßJiù!z´ÀŠÚdÛ³|åÍë_ífgˆTfæ*ܹ±DÔvÐk"Ø‘úïKˆ±±±®So¿ý=ßß8vô˜H:ŒŒŒpùòU¤m³!—Ãq]jµ: ˆÅ]´±øCñ…Ï)ÛÙgùæÞ¨^ÍÆBk©T*½¿;yò•ñ‰ÉïKaE_~ñ»ôô¬ãþÌ,ÓÓÓ”Je<Ï¡q¢.Éd’ÜúnÖu¯Cš—þ˜!ùµ$ÅO x£š‰ß÷礔³¬8@V)µehhèù¿œ>ýâ±»¹gžÙ/Žý½½½4M´n=mm ±PXäü…óœ~÷=&~MâÀ–F‹x×5oõ'º”R ­µ  ØT­Vw]½zå«W.ÿãI/ðz:;:£‘ˆ+ªõ`pc˜b¡„a°¸sçàèÏ®>y0q EézÿºæîÉ-¶”2l}\­™.€:€`K©Tê[XXØX­V;”RqcŒˆD"ÍX,VÎf³3ÙlöžeYwŸøÉì;‰ýkXºµˆº¡¹÷ÖÖÕÑk! àcPf€ÑL&“Èd2 ¶r€ÔVÍ@¡ªNJ„B¼òÙæÿY'nÛFIEND®B`‚openxenmanager/images/multipath.png0000644000175000017500000000124311636446664016265 0ustar rrsrrs‰PNG  IHDRàw=øjIDATxœí“]hÎQÇ?¿ó?ÿÿóþÏż“ejµ—&r#¡Êä­æÊK”—Þ–”Üiy_³P\-…)åBqaHä‚(V“iì%Ùöœósñ<«M{¸»}êWçüηó;¿—ãŒó¯c9·Z[´2 NO©™c¤½Ã«KwJ(ß r¶ØoŸ‚þ5â(œy2¿lÉ$þJ&ñj i‹œéíW'räD¿kÌÈÍþ(¬˜gmû4c*D•[C÷<|+à±êµNø€4«ž9¿2cŠëC»æB’îmÉ$þr&É®Ì5`ÉkíŸ2®…Ê;È»np?Á7‰Ü«‚’ás؆»j£ð¼ ð:›ýðÎùÒeQ "è`GÖ_­1¦RUk@= 3BýÕ0:¬Z÷ ÞË&”¬O¥>ìŽ*±zÒÎK›ómõέ+IqÞRy‹0ÿê¡õ‘RoÓ"=¯k/7f©ˆ@÷ýY‘AQQ~:¥Û“}èÜó"q5TFä:‘K»Tµ8ÉW †î*o& ÷x®P²+µ]É$¾%“øé¸k"ì–‹óV^!2©Š#ü‹w‹4÷ƒsà?BÿB8Z³FUb6m´Á¥ëéXo¤cß”Š¾Î…µuA0jÒFr l£Èå>Ðð/àËØW6¢É£ØØÔr‘C­Q8t; \«µ}Óa~¡çD¾ƒþGä} ¶”æ2,Ìš@ì"Øy30½ùTÒnÙÛ ®Qä °z$¼|˜UÆÈT(ªV3öÇv熦(ß–[3Îà”‘ÂÇ7`ËDIEND®B`‚openxenmanager/images/prop_general.gif0000644000175000017500000000141311636446664016713 0ustar rrsrrsGIF89aö~zgõ»ùº ÄŸ"Ê£&ɤ(Í©-ݶ>ÿÅþËþÌ ÿÌÿÒøæ÷åóã!ôæ,ýë7üë>ªžo´ªzÀ«UDzV̶S̶^Ü¿^¹sʺrïçBíéFïèJíæRïêTïéYñì_ÿóZéÈ`ÿÝaéÚ}ñìh÷ðhÿñ{~˜²·¾ž€—¯ƒœµƒ¼’¦ª†¢»‰¤¾‹¨½ÐÍŠñ×‡ëØ‘îâ“òë˜âç¿÷í¾­Á©Ì˜¹È“±×•´Û˜·Þ›ºÞœ¼á ¿äŸÁÔ¿ÚΡÃ×·ÓмØÒ¢Âã¦ÉãªÍåªÍé­Òì¾Úã²×í²Ùï¦Ãó©ÇôªÉô§Ãú¨Äû­Ìý³Íõ±Îü²Õö»Ó÷µÛó¸Úô²Òý»Òù¶Úü»Ýü¾â÷½ãü¾èüÃÝÇÄÜÚøòÂÁÔöÊÛöÂÞùÉÛúÝéäÇá÷ÎäúÀéýÐé÷ÖãúÖíüÝìûÿüíàêöäîûèïûãðüìòýÿþôôùýÿÿÿ!ù,þ€z‚ƒ„…†‡†qhkt„x)6eˆ„`a_[ƒx#9‰y‚y_–X¡r( %5…laZnqba¶^[j' 4„vabmÅbLEIIM!½$…y¶amE ÜLð —ÇRvô˜Ü#ß*T„„*'äc.W®L™Âõ&•)__…Œc§KÈ›YB "¿¢¨ØœÖÎ$C6©Xùý;Nš4¸‰+Ÿ;openxenmanager/oxcSERVER_vm_network.py0000644000175000017500000000776611636446664016670 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header class oxcSERVERvmnetwork: def vm_remove_interface(self, vm_ref, vif_ref): res = self.connection.VIF.unplug(self.session_uuid, vif_ref) if "Value" in res: self.track_tasks[res['Value']] = vm_ref self.vif_plug.append(res['Value']) res = self.connection.VIF.destroy(self.session_uuid, vif_ref) if "Value" in res: self.track_tasks[res['Value']] = vm_ref self.vif_plug.append(res['Value']) else: print res def fill_addinterface_network(self, list): list.clear() for network in self.all_network: if self.all_network[network]['bridge'] != "xapi0": #if self.all_pif[self.all_network[network]['PIFs'][0]]['bond_slave_of'] == "OpaqueRef:NULL": list.append([network, self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network ')]) def fill_editinterface_network(self, list, network_ref): list.clear() i = 0 current = 0 for network in self.all_network: if self.all_network[network]['bridge'] != "xapi0": #if self.all_pif[self.all_network[network]['PIFs'][0]]['bond_slave_of'] == "OpaqueRef:NULL": if network == network_ref: current = i list.append([network, self.all_network[network]['name_label'].replace('Pool-wide network associated with eth','Network ')]) i = i + 1 return current def vm_add_interface(self, vm_ref, network_ref, mac, limit): userdevices = [0] for vif in self.all_vms[vm_ref]['VIFs']: userdevices.append(self.all_vif[vif]['device']) vif_cfg = { 'uuid': '', 'allowed_operations': [], 'current_operations': [], 'device': str(int(max(userdevices))+1), 'MAC': '', 'MTU': '0', "qos_algorithm_type": "ratelimit", "qos_algorithm_params": {}, "other_config": {}, "MAC_autogenerated": "False", "currently_attached": False } if limit: vif_cfg["qos_algorithm_params"]["kbps"] = limit vif_cfg['network'] = network_ref vif_cfg['VM'] = vm_ref if mac: vif_cfg["MAC_autogenerated"] = "False" vif_cfg["MAC"] = mac else: vif_cfg["MAC_autogenerated"] = "True" self.flag_vif_plug = False res = self.connection.Async.VIF.create(self.session_uuid, vif_cfg) if "Value" in res: self.track_tasks[res['Value']] = vm_ref self.vif_plug.append(res['Value']) else: print "**", res openxenmanager/oxcSERVER_vm.py0000644000175000017500000001136311636446664015103 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- import xmlrpclib, urllib import asyncore, socket import select import gtk from os import chdir import platform import sys, shutil import datetime from threading import Thread from configobj import ConfigObj import xml.dom.minidom from operator import itemgetter import pdb import rrdinfo import time import gobject from messages import messages, messages_header from oxcSERVER_vm_network import * from oxcSERVER_vm_storage import * from oxcSERVER_vm_snapshot import * class oxcSERVERvm(oxcSERVERvmnetwork,oxcSERVERvmstorage,oxcSERVERvmsnapshot): def thread_import_vm(self, ref, file): Thread(target=self.import_vm, args=(ref, file)).start() return True def set_memory(self, ref, dynamicmin, dynamicmax, staticmax): actual_staticmin = self.all_vms[ref]["memory_static_min"] actual_staticmax = self.all_vms[ref]["memory_static_max"] res = self.connection.VM.set_memory_dynamic_range(self.session_uuid, ref, str(int(dynamicmin)*1024*1024), str(int(dynamicmax)*1024*1024)) if "Value" in res: if int(actual_staticmax) != int(int(staticmax)*1024*1024): res = self.connection.VM.set_memory_static_range(self.session_uid, ref, actual_staticmin, staticmax) if "Value" not in "OK": self.wine.show_error_dlg(str(res["ErrorDescription"])) else: self.wine.show_error_dlg(str(res["ErrorDescription"])) def set_memory_limits(self, ref, min, max, min2, max2): res = self.connection.VM.set_memory_limits(self.session_uuid, ref, str(int(min)*1024*1024), str(int(max)*1024*1024), str(int(min2)*1024*1024), str(int(max2)*1024*1024)) if "Value" in res: return "OK" else: self.wine.show_error_dlg(str(res["ErrorDescription"])) def copy_vm(self, ref, name, desc, sr=None, full=False): if full: res = self.connection.Async.VM.copy(self.session_uuid, ref, name, sr) if "Value" in res: self.track_tasks[res['Value']] = ref self.set_descriptions[res['Value']] = desc else: print res else: res = self.connection.Async.VM.clone(self.session_uuid, ref, name) if "Value" in res: self.track_tasks[res['Value']] = ref self.set_descriptions[res['Value']] = desc else: print res def fill_importstg(self, list): list.clear() i = 0 default_sr = 0 for sr in self.all_storage.keys(): storage = self.all_storage[sr] if storage['type'] != "iso" and storage['type'] != "udev": if self.default_sr == sr: default_sr = i if len(self.all_storage[sr]['PBDs']) == 0 or self.all_pbd[self.all_storage[sr]['PBDs'][0]]['currently_attached'] == False \ or len(self.all_storage[sr]['PBDs']) > 0 and self.all_storage[sr]["allowed_operations"].count("unplug") == 0: pass else: if self.default_sr == sr: list.append([gtk.gdk.pixbuf_new_from_file("images/storage_default_16.png"), sr, storage['name_label'], self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])) + " free of " + \ self.convert_bytes(storage['physical_size'])]) else: list.append([gtk.gdk.pixbuf_new_from_file("images/storage_shaped_16.png"), sr, storage['name_label'], self.convert_bytes(int(storage['physical_size'])-int(storage['virtual_allocation'])) + " free of " + \ self.convert_bytes(storage['physical_size'])]) i = i + 1 return default_sr openxenmanager/window_properties.py0000644000175000017500000016403211636446664016446 0ustar rrsrrs# ----------------------------------------------------------------------- # OpenXenManager # # Copyright (C) 2009 Alberto Gonzalez Rodriguez alberto@pesadilla.org # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MER- # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ----------------------------------------------------------------------- #!/usr/bin/env python import hashlib import gtk import xml.dom.minidom class oxcWindowProperties: """ Class to manage the properties window (vm, template, host, network, storage..) and window options """ changes = {} selected_prop_path = None freedevices = {} other_config = None def on_addcustomfield_clicked(self, widget, data=None): """ Function called when you press "Add" on custom fields window """ if self.builder.get_object("namecustomfields").get_text(): combocustomfields = self.builder.get_object("combocustomfields") listcombocustomfields = self.builder.get_object("listcombocustomfields") listcustomfields = self.builder.get_object("listcustomfields") name = self.builder.get_object("namecustomfields").get_text() ctype = listcombocustomfields.get_value(combocustomfields.get_active_iter(), 1) listcustomfields.append((["%s (%s)" % (name, ctype), name, ctype])) self.builder.get_object("namecustomfields").set_text("") def on_deletecustomfield_clicked(self, widget, data=None): listcustomfields = self.builder.get_object("listcustomfields") treecustomfields = self.builder.get_object("treecustomfields") selection = treecustomfields.get_selection() iter_ref = selection.get_selected()[1] if iter_ref: listcustomfields.remove(iter_ref) def on_bteditcustomfields_clicked(self, widget, data=None): """ Function called when you press "Edit Custom Fields..." """ listcustomfields = self.builder.get_object("listcustomfields") self.xc_servers[self.selected_host].fill_listcustomfields(listcustomfields) self.builder.get_object("wcustomfields").show() def on_acceptwcustomfields_clicked(self, widget, data=None): """ Function called when you press close button on custom fields window """ listcustomfields = self.builder.get_object("listcustomfields") xml = "" for i in range(listcustomfields.__len__()): iter_ref = listcustomfields.get_iter((i,)) xml = xml + '' % (listcustomfields.get_value(iter_ref, 1), listcustomfields.get_value(iter_ref, 2).split(" ")[0]) xml = xml + "" self.xc_servers[self.selected_host].set_pool_custom_fields(xml) self.builder.get_object("wcustomfields").hide() self.fill_custom_fields_table(add=True) def on_cancelwcustomfields_clicked(self, widget, data=None): """ Function called when you press close button on custom fields window """ self.builder.get_object("wcustomfields").hide() def on_acceptdialogoptions_clicked(self, widget, data=None): """ Function called when you apply changes on "options" dialog """ # Set in config If "save server passwords" is checked self.config["gui"]["save_password"] = str(self.builder.get_object("checksavepassword").get_active()) # If "save server passwords" is checked if self.builder.get_object("checksavepassword").get_active(): # Convert master password to md5 m = hashlib.md5() m.update(self.builder.get_object("txtmasterpassword").get_text()) # And set it on configuration self.config["gui"]["master_password"] = m.hexdigest() # Save configuration in disk self.config.write() # Hide options dialog self.builder.get_object("dialogoptions").hide() def on_radiologlocal_toggled(self, widget, data=None): """ Function called when you set server system log destination to "local" """ # Set if enabled or not depends radiologlocal state self.builder.get_object("lbllogserver").set_sensitive(not widget.get_active()) self.builder.get_object("txtlogserver").set_sensitive(not widget.get_active()) def on_canceldialogoptions_clicked(self, widget, data=None): """ Function called when you cancel changes on "options" dialog """ # Hide options dialog self.builder.get_object("dialogoptions").hide() def on_btvmpropcancel_activate(self, widget, data=None): """ Function called when you cancel changes on "properties" window """ # Hide properties window self.builder.get_object("dialogvmprop").hide() # And unset used variables self.selected_widget = None self.changes = {} self.freedevices = {} def on_treebootorder_button_press_event(self, widget, event): """ Function called when you select a element on bootorder tree """ x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo iter = self.builder.get_object("listpropbootorder").get_iter(path) # If is not separated line if self.builder.get_object("listpropbootorder").get_value(iter, 2): # If is the first disable "up" button, else enable it self.builder.get_object("btmoveup").set_sensitive(path[0] != 0) # If is the last disable "down" button, else enable it self.builder.get_object("btmovedown").set_sensitive(path[0] != 0) else: # Disable both buttons self.builder.get_object("btmoveup").set_sensitive(False) self.builder.get_object("btmovedown").set_sensitive(False) def on_btmoveup_clicked(self, widget, data=None): """ Function called when you press "Up" button """ rows = self.builder.get_object("treebootorder").get_selection().get_selected_rows()[1][0] # Get actual iter iter1 = self.builder.get_object("listpropbootorder").get_iter(rows) # Get below iter iter2 = self.builder.get_object("listpropbootorder").get_iter((rows[0]-1,)) # Swap self.builder.get_object("listpropbootorder").swap(iter1, iter2) if rows[0]-1 == 0: # If is the first now, disable "up" button and enable "down" button self.builder.get_object("btmoveup").set_sensitive(False) self.builder.get_object("btmovedown").set_sensitive(True) def on_btmovedown_clicked(self, widget, data=None): """ Function called when you press "Down" button """ rows = self.builder.get_object("treebootorder").get_selection().get_selected_rows()[1][0] # Get actual iter iter1 = self.builder.get_object("listpropbootorder").get_iter(rows) # Get above iter iter2 = self.builder.get_object("listpropbootorder").get_iter((rows[0]+1,)) self.builder.get_object("listpropbootorder").swap(iter1, iter2) if (rows[0]+1) == 3: # If is the last now, disable "down" button and enable "up" button self.builder.get_object("btmovedown").set_sensitive(False) self.builder.get_object("btmoveup").set_sensitive(True) def prop_visible_func(self, model, iter, user_data=None): """ Function to know if a menu element should be showed or hidden """ # aka contains properties element (vm, host, storage, network or vdi) aka = self.listprop.get_value(iter, 2) # List of menu options to show vm = ["general", "custom", "cpumemory", "startup", "homeserver"] host = ["general","custom", "multipath", "logdest"] storage = ["general","custom"] network = ["general","custom","networksettings"] vdi = ["general","custom","sizelocation","stgvm"] pool = ["general", "custom"] # For different elements show or hidde menu options # If return false, element will be not showed, else will be showed if self.selected_type == "host": if self.selected_widget and gtk.Buildable.get_name(self.selected_widget) == "bthostnetworkproperties": # If element is not on "Network" array if not network.count(aka): # hide it return False else: if not host.count(aka): return False elif self.selected_widget and (gtk.Buildable.get_name(self.selected_widget) == "btstorageproperties" or gtk.Buildable.get_name(self.selected_widget) == "btstgproperties"): # same if not vdi.count(aka): return False elif self.selected_widget and gtk.Buildable.get_name(self.selected_widget) == "menuitem_server_prop": # same if not host.count(aka): return False elif self.selected_type == "storage": # same if not storage.count(aka): return False elif self.selected_type == "pool": # same if not pool.count(aka): return False elif self.selected_type == "vm" or self.selected_type == "template" or self.selected_type == "custom_template": if "HVM_shadow_multiplier" in self.xc_servers[self.selected_host].all_vms[self.selected_ref] and \ "HVM_boot_policy" in self.xc_servers[self.selected_host].all_vms[self.selected_ref] and \ self.xc_servers[self.selected_host].all_vms[self.selected_ref]["HVM_boot_policy"]: vm.append("advancedoptions") # same if not vm.count(aka): return False return True def on_combostgmodeposition_changed(self, widget, data=None): """ Function called when you change element on combostgmode or combostgposition """ #TODO: comment code if self.selected_widget: listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": liststorage = self.builder.get_object("liststg") treestorage = self.builder.get_object("treestg") column = 0 else: liststorage = self.builder.get_object("listvmstorage") treestorage = self.builder.get_object("treevmstorage") column = 10 selection = treestorage.get_selection() iter = selection.get_selected()[1] ref = liststorage.get_value(iter,column) path = self.selected_prop_path if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": ref = self.xc_servers[self.selected_host].all_vdi[ref]['VBDs'][path[0]-9] device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] else: device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] if ref not in self.changes: self.changes[ref] = {} if gtk.Buildable.get_name(widget) == "combostgmode": if mode != widget.get_active_text(): self.changes[ref]['mode'] = widget.get_active_text() else: if "mode" in self.changes[ref]: del self.changes[ref]['mode'] else: if device != widget.get_active_text(): self.changes[ref]['position'] = widget.get_active_text() else: if "position" in self.changes[ref]: del self.changes[ref]['position'] if device != widget.get_active_text() and \ not self.freedevices[vm_ref].count(widget.get_active_text()): self.builder.get_object("lblinuse").show() self.builder.get_object("btvmpropaccept").set_sensitive(False) else: self.builder.get_object("lblinuse").hide() self.builder.get_object("btvmpropaccept").set_sensitive(True) def on_checkisbootable_clicked(self, widget, data=None): """ Function called when you change bootable check state """ if self.selected_widget: listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": liststorage = self.builder.get_object("liststg") treestorage = self.builder.get_object("treestg") column = 0 else: liststorage = self.builder.get_object("listvmstorage") treestorage = self.builder.get_object("treevmstorage") column = 10 selection = treestorage.get_selection() iter = selection.get_selected()[1] ref = liststorage.get_value(iter,column) path = self.selected_prop_path if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": ref = self.xc_servers[self.selected_host].all_vdi[ref]['VBDs'][path[0]-9] bootable = self.xc_servers[self.selected_host].all_vbd[ref]['bootable'] if ref not in self.changes: self.changes[ref] = {} if bootable != widget.get_active(): self.changes[ref]['bootable'] = widget.get_active() def on_acceptdialogsyslogempty_clicked(self, widget, data=None): """ If you set a empty syslog server, dialogsyslogempty is showed This function is called when you accept the dialog """ self.builder.get_object("dialogsyslogempty").hide() def on_spinpropvmprio_change_value(self, widget, data, data2): """ Function called when "Priority" spin value is changed """ # spin get value from 0 to 10, but priority goes from 1 to 64000 (less or more) # then 2 ^ (2 * spin value) gets the real priority self.builder.get_object("spinpropvmprio").set_value(2**(2*int(data2))) def on_treeprop_button_press_event(self,widget, event): """ Function called when you select a left property option """ # TODO: comment code x = int(event.x) y = int(event.y) time = event.time pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo widget.grab_focus() widget.set_cursor( path, col, 0) path = self.propmodelfilter.convert_path_to_child_path(path) self.selected_prop_path = path iter = self.listprop.get_iter(path) if path[0] < 9 or self.listprop.get_value(iter, 2) == "advancedoptions": self.builder.get_object("tabprops").set_current_page(self.listprop.get_value(iter, 3)) else: self.builder.get_object("tabprops").set_current_page(9) if path[0] > 8 and self.listprop.get_value(iter, 2) != "advancedoptions": if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": liststorage = self.builder.get_object("liststg") treestorage = self.builder.get_object("treestg") column = 0 else: liststorage = self.builder.get_object("listvmstorage") treestorage = self.builder.get_object("treevmstorage") column = 10 selection = treestorage.get_selection() iter = selection.get_selected()[1] ref = liststorage.get_value(iter,column) if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": ref = self.xc_servers[self.selected_host].all_vdi[ref]['VBDs'][path[0]-9] vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] type = self.xc_servers[self.selected_host].all_vbd[ref]['type'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] bootable = self.xc_servers[self.selected_host].all_vbd[ref]['bootable'] vm_name = self.xc_servers[self.selected_host].all_vms[vm_ref]['name_label'] self.builder.get_object("lblpropstgvm").set_label(vm_name) if mode == "RW": self.builder.get_object("combostgmode").set_active(0) else: self.builder.get_object("combostgmode").set_active(1) if device[0] != "x": self.builder.get_object("combostgposition").set_sensitive(True) self.builder.get_object("combostgposition").set_active(int(device)) else: self.builder.get_object("combostgposition").set_sensitive(False) self.builder.get_object("combostgmode").set_sensitive(type == "Disk") self.builder.get_object("checkisbootable").set_active(bootable) else: device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] bootable = self.xc_servers[self.selected_host].all_vbd[ref]['bootable'] vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] vm_name = self.xc_servers[self.selected_host].all_vms[vm_ref]['name_label'] self.builder.get_object("lblpropstgvm").set_label(vm_name) if mode == "RW": self.builder.get_object("combostgmode").set_active(0) else: self.builder.get_object("combostgmode").set_active(1) self.builder.get_object("combostgposition").set_active(int(device)) self.builder.get_object("checkisbootable").set_active(bootable) def on_btvmpropaccept_activate(self, widget, data=None): """ Function called when you accept window properties """ # TODO: comment codea if self.selected_widget and gtk.Buildable.get_name(self.selected_widget) == "bthostnetworkproperties": liststorage = self.builder.get_object("listhostnetwork") treestorage = self.builder.get_object("treehostnetwork") selection = treestorage.get_selection() iter = selection.get_selected()[1] ref = liststorage.get_value(iter,7) network = self.xc_servers[self.selected_host].all_network[ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if self.builder.get_object("txtpropvmname").get_text() != network['name_label']: self.xc_servers[self.selected_host].set_network_name_label(ref, self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != network['name_description']: self.xc_servers[self.selected_host].set_network_name_description(ref, tb.get_text(tb.get_start_iter(), tb.get_end_iter())) if "automatic" in network['other_config'] and network['other_config']['automatic'] == "true": if self.builder.get_object("checknetworkautoadd").get_active() == False: self.xc_servers[self.selected_host].set_network_automatically(ref, self.builder.get_object("checknetworkautoadd").get_active()) else: if self.builder.get_object("checknetworkautoadd").get_active(): self.xc_servers[self.selected_host].set_network_automatically(ref, self.builder.get_object("checknetworkautoadd").get_active()) self.changes = {} other_config = network["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_network_other_config(self.selected_ref, other_config) elif self.selected_widget and (gtk.Buildable.get_name(self.selected_widget) == "btstgproperties" or \ gtk.Buildable.get_name(self.selected_widget) == "btstorageproperties"): if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": liststorage = self.builder.get_object("liststg") treestorage = self.builder.get_object("treestg") column = 0 else: liststorage = self.builder.get_object("listvmstorage") treestorage = self.builder.get_object("treevmstorage") column = 10 selection = treestorage.get_selection() iter = treestorage.get_selection().get_selected()[1] ref = liststorage.get_value(iter,column) if gtk.Buildable.get_name(self.selected_widget) == "btstgproperties": vdi_ref = ref else: vdi_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VDI'] vdi_sr = self.xc_servers[self.selected_host].all_vdi[vdi_ref]['SR'] vdi = self.xc_servers[self.selected_host].all_vdi[vdi_ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if self.builder.get_object("txtpropvmname").get_text() != vdi['name_label']: self.xc_servers[self.selected_host].set_vdi_name_label(vdi_ref,self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != vdi['name_description']: self.xc_servers[self.selected_host].set_vdi_name_description(vdi_ref,tb.get_text(tb.get_start_iter(), tb.get_end_iter())) if self.builder.get_object("spinvdisize").get_value() != float(vdi['virtual_size'])/1024/1024/1024: size = self.builder.get_object("spinvdisize").get_value()*1024*1024*1024 self.xc_servers[self.selected_host].resize_vdi(vdi_ref,size) for ref in self.changes: if "position" in self.changes[ref]: self.xc_servers[self.selected_host].set_vbd_userdevice(ref, self.changes[ref]['position']) if "mode" in self.changes[ref]: self.xc_servers[self.selected_host].set_vbd_mode(ref, self.changes[ref]['mode']) if "bootable" in self.changes[ref]: self.xc_servers[self.selected_host].set_vbd_bootable(ref, self.changes[ref]['bootable']) self.changes = {} other_config = vdi["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_vdi_other_config(self.selected_ref, other_config) elif self.selected_type == "host" or (self.selected_widget and gtk.Buildable.get_name(self.selected_widget) == "menuitem_server_prop"): vm = self.xc_servers[self.selected_host].all_hosts[self.selected_ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if "syslog_destination" in vm["logging"] == self.builder.get_object("radiologlocal").get_active(): if self.builder.get_object("radiologlocal").get_active(): self.xc_servers[self.selected_host].set_host_log_destination(self.selected_ref, None) else: if self.builder.get_object("txtlogserver").get_text(): self.xc_servers[self.selected_host].set_host_log_destination(self.selected_ref, self.builder.get_object("txtlogserver").get_text()) else: self.builder.get_object("dialogsyslogempty").show() return if self.builder.get_object("txtpropvmname").get_text() != vm['name_label']: self.xc_servers[self.selected_host].set_host_name_label(self.selected_ref, self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != vm['name_description']: self.xc_servers[self.selected_host].set_host_name_description(self.selected_ref, tb.get_text(tb.get_start_iter(), tb.get_end_iter())) other_config = vm["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_host_other_config(self.selected_ref, other_config) elif self.selected_type == "storage": stg = self.xc_servers[self.selected_host].all_storage[self.selected_ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if self.builder.get_object("txtpropvmname").get_text() != stg['name_label']: self.xc_servers[self.selected_host].set_storage_name_label(self.selected_ref, self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != stg['name_description']: self.xc_servers[self.selected_host].set_storage_name_description(self.selected_ref, tb.get_text(tb.get_start_iter(), tb.get_end_iter())) other_config = stg["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_storage_other_config(self.selected_ref, other_config) elif self.selected_type == "pool": pool = self.xc_servers[self.selected_host].all_pools[self.selected_ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if self.builder.get_object("txtpropvmname").get_text() != pool['name_label']: self.xc_servers[self.selected_host].set_pool_name_label(self.selected_ref, self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != pool['name_description']: self.xc_servers[self.selected_host].set_pool_name_description(self.selected_ref, tb.get_text(tb.get_start_iter(), tb.get_end_iter())) other_config = pool["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_pool_other_config(self.selected_ref, other_config) elif self.selected_type == "vm" or self.selected_type == "template" or self.selected_type == "custom_template": vm = self.xc_servers[self.selected_host].all_vms[self.selected_ref] tb = self.builder.get_object("txtpropvmdesc").get_buffer() if self.builder.get_object("txtpropvmname").get_text() != vm['name_label']: self.xc_servers[self.selected_host].set_vm_name_label(self.selected_ref, self.builder.get_object("txtpropvmname").get_text()) if tb.get_text(tb.get_start_iter(), tb.get_end_iter()) != vm['name_description']: self.xc_servers[self.selected_host].set_vm_name_description(self.selected_ref, tb.get_text(tb.get_start_iter(), tb.get_end_iter())) if int(self.builder.get_object("spinpropvmmem").get_value()) != int(vm["memory_dynamic_min"])/1024/1024: self.xc_servers[self.selected_host].set_vm_memory(self.selected_ref, self.builder.get_object("spinpropvmmem").get_value()) if int(self.builder.get_object("spinpropvmvcpus").get_value()) != int(vm["VCPUs_at_startup"]): self.xc_servers[self.selected_host].set_vm_vcpus(self.selected_ref, self.builder.get_object("spinpropvmvcpus").get_value()) if "weight" in vm["VCPUs_params"]: if self.builder.get_object("spinpropvmprio").get_value() != float(vm["VCPUs_params"]["weight"]): self.xc_servers[self.selected_host].set_vm_prio(self.selected_ref, self.builder.get_object("spinpropvmprio").get_value()) else: if self.builder.get_object("spinpropvmprio").get_value() != float(256): self.xc_servers[self.selected_host].set_vm_prio(self.selected_ref, self.builder.get_object("spinpropvmprio").get_value()) if "auto_poweron" in vm['other_config'] and vm['other_config']["auto_poweron"] == "true": if self.builder.get_object("checkvmpropautostart").get_active() == False: self.xc_servers[self.selected_host].set_vm_poweron(self.selected_ref, self.builder.get_object("checkvmpropautostart").get_active()) else: if self.builder.get_object("checkvmpropautostart").get_active(): self.xc_servers[self.selected_host].set_vm_poweron(self.selected_ref, self.builder.get_object("checkvmpropautostart").get_active()) if not vm['HVM_boot_policy']: if self.builder.get_object("txtvmpropparams").get_text() != vm['PV_args']: self.xc_servers[self.selected_host].set_vm_bootpolicy(self.selected_ref, self.builder.get_object("txtvmpropparams").get_text()) else: order = '' for i in range(0,4): iter = self.builder.get_object("listpropbootorder").get_iter((i,0)) order += self.builder.get_object("listpropbootorder").get_value(iter,0) if self.builder.get_object("listpropbootorder").get_value(iter,2) == False: break if order != vm['HVM_boot_params']['order']: self.xc_servers[self.selected_host].set_vm_boot_params(self.selected_ref, order) shared = True for vbd_ref in vm['VBDs']: vbd = self.xc_servers[self.selected_host].get_vbd(vbd_ref) if vbd['VDI'] != "OpaqueRef:NULL" and vbd['VDI']: vdi = self.xc_servers[self.selected_host].get_vdi(vbd['VDI']) if not self.xc_servers[self.selected_host].get_storage(vdi['SR'])["shared"]: shared = False break if self.builder.get_object("radioautohome").get_active() and (vm["affinity"] != "OpaqueRef:NULL"): self.xc_servers[self.selected_host].set_vm_affinity(self.selected_ref, "OpaqueRef:NULL") if self.builder.get_object("radiomanualhome").get_active(): listhomeserver = self.builder.get_object("listhomeserver") treehomeserver = self.builder.get_object("treehomeserver") iter = treehomeserver.get_selection().get_selected()[1] affinity = listhomeserver.get_value(iter, 0) if affinity != vm["affinity"]: self.xc_servers[self.selected_host].set_vm_affinity(self.selected_ref, affinity) other_config = vm["other_config"] change = False for cfield in self.vboxchildtext: if "XenCenter.CustomFields." + cfield in other_config: if self.vboxchildtext[cfield].get_text() != other_config["XenCenter.CustomFields." + cfield]: change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() else: if self.vboxchildtext[cfield].get_text(): change = True other_config["XenCenter.CustomFields." + cfield] = self.vboxchildtext[cfield].get_text() if change: self.xc_servers[self.selected_host].set_vm_other_config(self.selected_ref, other_config) if "HVM_shadow_multiplier" in vm: if self.builder.get_object("optimizegeneraluse").get_active(): multiplier = "1.00" elif self.builder.get_object("optimizeforxenapp").get_active(): multiplier = "4.00" else: multiplier = self.builder.get_object("memorymultiplier").get_text() if multiplier != str(vm["HVM_shadow_multiplier"]): self.xc_servers[self.selected_host].set_vm_memory_multiplier(self.selected_ref, multiplier) self.builder.get_object("dialogvmprop").hide() self.selected_widget = None def fill_btstorage_properties(self, widget): self.selected_widget = widget self.propmodelfilter.refilter() listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) if treeprop.get_selection(): treeprop.get_selection().select_path((0,)) if gtk.Buildable.get_name(widget) == "btstorageproperties": liststorage = self.builder.get_object("listvmstorage") treestorage = self.builder.get_object("treevmstorage") column = 10 else: liststorage = self.builder.get_object("liststg") treestorage = self.builder.get_object("treestg") column = 0 selection = treestorage.get_selection() if selection.get_selected()[1] != None: self.builder.get_object("vmfreedevices").show() #self.builder.get_object("dialogvmprop").show() for i in range(listprop.__len__()-1,8,-1): iter = listprop.get_iter((i,)) listprop.remove(iter) iter = selection.get_selected()[1] ref = liststorage.get_value(iter,column) #print self.xc_servers[self.selected_host].all_vdi[ref] if gtk.Buildable.get_name(widget) == "btstgproperties": vdi_ref = ref else: vdi_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VDI'] vdi_sr = self.xc_servers[self.selected_host].all_vdi[vdi_ref]['SR'] vdi = self.xc_servers[self.selected_host].all_vdi[vdi_ref] stg_name = self.xc_servers[self.selected_host].all_storage[vdi_sr]['name_label'] stg_pbds = self.xc_servers[self.selected_host].all_storage[vdi_sr]['PBDs'] hosts = [] for stg_pbd in stg_pbds: stg_host = self.xc_servers[self.selected_host].all_pbd[stg_pbd]['host'] hosts.append( self.xc_servers[self.selected_host].all_hosts[stg_host]['name_label']) iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + vdi['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(vdi['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(vdi['name_description']) iter = listprop.get_iter((8,)) subtext = self.convert_bytes(vdi['virtual_size']) + "," + stg_name + " on " + ",".join(hosts) listprop.set_value(iter, 1, "Size and Location\n " + subtext + "") self.builder.get_object("adjvdisize").set_lower(float(vdi['virtual_size'])/1024/1024/1024) self.builder.get_object("spinvdisize").set_value(float(vdi['virtual_size'])/1024/1024/1024) listvdilocation = self.builder.get_object("listvdilocation") pos = self.xc_servers[self.selected_host].fill_vdi_location(vdi_sr, listvdilocation) self.builder.get_object("spinvdisize").set_sensitive(vdi['allowed_operations'].count("resize")) if gtk.Buildable.get_name(widget) == "btstgproperties": i = 9 vbds = len(self.xc_servers[self.selected_host].all_vdi[ref]['VBDs']) if vbds: parts = float(1)/vbds else: parts = 1 self.builder.get_object("progressfreedevices").set_pulse_step(parts) update = 0 for ref in self.xc_servers[self.selected_host].all_vdi[ref]['VBDs']: vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] listprop.append([gtk.gdk.pixbuf_new_from_file("images/prop_stgvm.png"), "NAME", "stgvm", i]) iter = listprop.get_iter((i,)) mode = "Read / Write" if mode == "RW" else "Read Only" vm_name = self.xc_servers[self.selected_host].all_vms[vm_ref]['name_label'] subtext = "Device %s, (%s)" % (device, mode) listprop.set_value(iter, 1, "" + vm_name + "\n " + subtext + "") i = i + 1 self.freedevices[vm_ref] = self.xc_servers[self.selected_host].get_allowed_vbd_devices(vm_ref) update = update + parts self.builder.get_object("progressfreedevices").pulse() else: vm_ref = self.xc_servers[self.selected_host].all_vbd[ref]['VM'] device = self.xc_servers[self.selected_host].all_vbd[ref]['userdevice'] mode = self.xc_servers[self.selected_host].all_vbd[ref]['mode'] listprop.append([gtk.gdk.pixbuf_new_from_file("images/prop_stgvm.png"), "NAME", "stgvm", 9]) iter = listprop.get_iter((9,)) mode = "Read / Write" if mode == "RW" else "Read Only" vm_name = self.xc_servers[self.selected_host].all_vms[vm_ref]['name_label'] subtext = "Device %s, (%s)" % (device, mode) listprop.set_value(iter, 1, "" + vm_name + "\n " + subtext + "") self.freedevices[vm_ref] = self.xc_servers[self.selected_host].get_allowed_vbd_devices(vm_ref) self.builder.get_object("progressfreedevices").set_pulse_step(1) self.builder.get_object("progressfreedevices").pulse() self.builder.get_object("vmfreedevices").hide() self.builder.get_object("dialogvmprop").show() return self.xc_servers[self.selected_host].all_storage[vdi_sr]['other_config'] def fill_host_network_properties(self, widget): self.selected_widget = widget self.propmodelfilter.refilter() self.builder.get_object("tabprops").set_current_page(0) listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) treeprop.get_selection().select_path((0,)) liststorage = self.builder.get_object("listhostnetwork") treestorage = self.builder.get_object("treehostnetwork") selection = treestorage.get_selection() if selection.get_selected()[1] != None: self.builder.get_object("dialogvmprop").show() iter = selection.get_selected()[1] network = self.xc_servers[self.selected_host].all_network[liststorage.get_value(iter,7)] iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + network['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(network['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(network['name_description']) if "automatic" in network['other_config'] and network['other_config']['automatic'] == "true": self.builder.get_object("checknetworkautoadd").set_active(True) else: self.builder.get_object("checknetworkautoadd").set_active(False) return network["other_config"] def fill_server_properties(self, widget): self.selected_widget = widget self.propmodelfilter.refilter() self.builder.get_object("tabprops").set_current_page(0) listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) treeprop.get_selection().select_path((0,)) if gtk.Buildable.get_name(widget) == "menuitem_server_prop": ref = self.treestore.get_value(self.treestore.iter_parent(self.selected_iter),6) if ref in self.xc_servers[self.selected_host].all_hosts: vm = self.xc_servers[self.selected_host].all_hosts[ref] else: vm = self.xc_servers[self.selected_host].all_hosts[self.selected_ref] else: vm = self.xc_servers[self.selected_host].all_hosts[self.selected_ref] iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + vm['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(vm['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(vm['name_description']) if "syslog_destination" in vm['logging']: self.builder.get_object("radiologlocal").set_active(False) self.builder.get_object("radiologremote").set_active(True) self.builder.get_object("txtlogserver").set_text(vm["logging"]["syslog_destination"]) else: self.builder.get_object("radiologlocal").set_active(True) self.builder.get_object("radiologremote").set_active(False) self.builder.get_object("txtlogserver").set_text("") self.builder.get_object("dialogvmprop").show() return vm["other_config"] def fill_storage_properties(self): listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) treeprop.get_selection().select_path((0,)) stg = self.xc_servers[self.selected_host].all_storage[self.selected_ref] iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + stg['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(stg['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(stg['name_description']) self.builder.get_object("dialogvmprop").show() self.builder.get_object("tabprops").set_current_page(0) return stg["other_config"] def fill_pool_properties(self): self.builder.get_object("tabprops").set_current_page(0) listprop = self.builder.get_object("listprop") treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) treeprop.get_selection().select_path((0,)) pool = self.xc_servers[self.selected_host].all_pools[self.selected_ref] iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + pool['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(pool['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(pool['name_description']) self.builder.get_object("dialogvmprop").show() return pool["other_config"] def fill_vm_properties(self): listprop = self.builder.get_object("listprop") vm = self.xc_servers[self.selected_host].all_vms[self.selected_ref] # Name, Description, Folder and Tags iter = listprop.get_iter((0,)) listprop.set_value(iter, 1, "General\n " + vm['name_label'] + "") self.builder.get_object("txtpropvmname").set_text(vm['name_label']) self.builder.get_object("txtpropvmdesc").get_buffer().set_text(vm['name_description']) if "folder" in vm['other_config']: self.builder.get_object("lblpropvmfolder").set_label(vm['other_config']['folder']) else: self.builder.get_object("lblpropvmfolder").set_label("") if vm["tags"]: self.builder.get_object("lblpropvmtags").set_label(", ".join(vm["tags"])) else: self.builder.get_object("lblpropvmtags").set_label("") # Memory and VCPUS iter = listprop.get_iter((2,)) listprop.set_value(iter, 1, "CPU and Memory\n " \ + "%s VCPU(s) and %s RAM" % (vm["VCPUs_at_startup"], self.convert_bytes(vm["memory_dynamic_max"])) + "") self.builder.get_object("spinpropvmmem").set_value(float(vm["memory_dynamic_min"])/1024/1024) self.builder.get_object("spinpropvmvcpus").set_value(float(vm["VCPUs_at_startup"])) if "weight" in vm["VCPUs_params"]: self.builder.get_object("spinpropvmprio").set_value(float(vm["VCPUs_params"]["weight"])) weight = float(vm["VCPUs_params"]["weight"]) if weight == 1: self.builder.get_object("scalepropvmprio").set_value(0) elif weight <= 4: self.builder.get_object("scalepropvmprio").set_value(1) elif weight <= 16: self.builder.get_object("scalepropvmprio").set_value(2) elif weight <= 64: self.builder.get_object("scalepropvmprio").set_value(3) elif weight <= 256: self.builder.get_object("scalepropvmprio").set_value(4) elif weight <= 1024: self.builder.get_object("scalepropvmprio").set_value(5) elif weight <= 4096: self.builder.get_object("scalepropvmprio").set_value(6) elif weight <= 16384: self.builder.get_object("scalepropvmprio").set_value(7) else: self.builder.get_object("scalepropvmprio").set_value(8) else: self.builder.get_object("spinpropvmprio").set_value(256) self.builder.get_object("scalepropvmprio").set_value(4) # Boot Options iter = listprop.get_iter((3,)) if "auto_poweron" in vm['other_config'] and vm['other_config']["auto_poweron"] == "true": listprop.set_value(iter, 1, "Startup Options\n Auto-start on server boot") else: listprop.set_value(iter, 1, "Startup Options\n None defined") if "auto_poweron" in vm['other_config'] and vm['other_config']["auto_poweron"] == "true": self.builder.get_object("checkvmpropautostart").set_active(True) else: self.builder.get_object("checkvmpropautostart").set_active(False) if not vm['HVM_boot_policy']: self.builder.get_object("txtvmpropparams").set_text(vm['PV_args']) self.builder.get_object("lblpropvmorder").hide() self.builder.get_object("scrollwindowbootorder").hide() self.builder.get_object("btmoveup").hide() self.builder.get_object("btmovedown").hide() self.builder.get_object("lblvmpropparams").show() self.builder.get_object("txtvmpropparams").show() else: self.builder.get_object("lblpropvmorder").show() self.builder.get_object("scrollwindowbootorder").show() self.builder.get_object("btmoveup").show() self.builder.get_object("btmovedown").show() self.builder.get_object("lblvmpropparams").hide() self.builder.get_object("txtvmpropparams").hide() listbootorder = self.builder.get_object("listpropbootorder") listbootorder.clear() for param in list(vm['HVM_boot_params']['order']): if param == 'c': listbootorder.append([param, "Hard Disk", True]) elif param == 'd': listbootorder.append([param, "DVD-Drive", True]) elif param == 'n': listbootorder.append([param, "Network", True]) listbootorder.append(["","-------------- VM will not boot from devices below this line ------------", False]) if vm['HVM_boot_params']['order'].count("c") == 0: listbootorder.append(["c", "Hard Disk", True]) if vm['HVM_boot_params']['order'].count("d") == 0: listbootorder.append(["d", "DVD-Drive", True]) if vm['HVM_boot_params']['order'].count("n") == 0: listbootorder.append(["n", "Network", True]) # Home Server || TODO shared iter = listprop.get_iter((4,)) if vm['affinity'] != "OpaqueRef:NULL" and vm['affinity'] in self.xc_servers[self.selected_host].all_hosts: affinity = self.xc_servers[self.selected_host].all_hosts[vm['affinity']] listprop.set_value(iter, 1, "Home server\n " + affinity['name_label'] + "") else: listprop.set_value(iter, 1, "Home server\n None defined") shared = True for vbd_ref in vm['VBDs']: vbd = self.xc_servers[self.selected_host].get_vbd(vbd_ref) if vbd['VDI'] != "OpaqueRef:NULL" and vbd['VDI']: vdi = self.xc_servers[self.selected_host].get_vdi(vbd['VDI']) if not vdi or self.xc_servers[self.selected_host].get_storage(vdi['SR'])["type"] != "nfs": shared = False break self.builder.get_object("radioautohome").set_sensitive(shared) self.builder.get_object("radioautohome").set_active(shared) self.builder.get_object("radiomanualhome").set_active(not shared) if shared: if vm["affinity"] == "OpaqueRef:NULL": self.builder.get_object("radioautohome").set_active(True) else: self.builder.get_object("radiomanualhome").set_active(True) listhomeserver = self.builder.get_object("listhomeserver") server = self.xc_servers[self.selected_host].fill_listhomeserver(listhomeserver, vm["affinity"]) treehomeserver = self.builder.get_object("treehomeserver") treehomeserver.set_cursor((0,), treehomeserver.get_column(0)) treehomeserver.get_selection().select_path((0,)) if "HVM_shadow_multiplier" in vm: self.builder.get_object("memorymultiplier").set_text(str(vm["HVM_shadow_multiplier"])) if float(vm["HVM_shadow_multiplier"]) == 1.00: self.builder.get_object("optimizegeneraluse").set_active(True) elif float(vm["HVM_shadow_multiplier"]) == 4.00: self.builder.get_object("optimizeforxenapp").set_active(True) else: self.builder.get_object("optimizemanually").set_active(True) self.propmodelfilter.refilter() # Show VM window properties self.builder.get_object("tabprops").set_current_page(0) treeprop = self.builder.get_object("treeprop") treeprop.set_cursor((0,), treeprop.get_column(0)) treeprop.get_selection().select_path((0,)) self.builder.get_object("dialogvmprop").show() return vm["other_config"] def set_custom_fields_values(self): for config in self.other_config: if "XenCenter.CustomFields." in config: if config[23:] in self.vboxchildtext: self.vboxchildtext[config[23:]].set_text(self.other_config[config]) def fill_custom_fields_table(self, add=False): pool_ref = self.xc_servers[self.selected_host].all_pools.keys()[0] self.vboxchildtext = {} if "XenCenter.CustomFields" in self.xc_servers[self.selected_host].all_pools[pool_ref]["gui_config"]: for ch in self.builder.get_object("vboxcustomfields").get_children(): self.builder.get_object("vboxcustomfields").remove(ch) dom = xml.dom.minidom.parseString( self.xc_servers[self.selected_host].all_pools[pool_ref]["gui_config"]["XenCenter.CustomFields"]) for node in dom.getElementsByTagName("CustomFieldDefinition"): name = node.attributes.getNamedItem("name").value if name not in self.vboxchildtext: vboxframe = gtk.Frame() vboxframe.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) vboxframe.set_size_request(500,30) vboxchild = gtk.Fixed() vboxchild.set_size_request(500,30) vboxevent = gtk.EventBox() vboxevent.add(vboxchild) vboxchildlabel = gtk.Label() vboxchildlabel.set_selectable(True) vboxchildlabel.set_label(name) vboxchild.put(vboxchildlabel, 5, 5) self.vboxchildtext[name] = gtk.Entry() self.vboxchildtext[name].set_size_request(200,20) vboxchild.put(self.vboxchildtext[name], 300, 5) vboxevent.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) self.builder.get_object("vboxcustomfields").pack_start(vboxevent, False, False, 0) self.builder.get_object("vboxcustomfields").show_all() if add: self.set_custom_fields_values() def on_properties_activate(self, widget, data=None): """ Function called when you click on "Properties" window/menuitem for all elements """ self.fill_custom_fields_table() # TODO: comment code self.propmodelfilter.refilter() self.builder.get_object("tabprops").set_current_page(0) if gtk.Buildable.get_name(widget) == "btstorageproperties" or gtk.Buildable.get_name(widget) == "btstgproperties": other_config = self.fill_btstorage_properties(widget) elif gtk.Buildable.get_name(widget) == "bthostnetworkproperties": other_config = self.fill_host_network_properties(widget) elif gtk.Buildable.get_name(widget) == "menuitem_server_prop" or self.selected_type == "host": other_config = self.fill_server_properties(widget) elif self.selected_type == "storage": other_config = self.fill_storage_properties() elif self.selected_type == "pool": other_config = self.fill_pool_properties() elif self.selected_type == "vm" or self.selected_type == "template" or self.selected_type == "custom_template": self.selected_widget = widget other_config = self.fill_vm_properties() self.other_config = other_config self.set_custom_fields_values() openxenmanager/xva.py0000644000175000017500000022750211636446664013463 0ustar rrsrrs#!/usr/bin/env python ## Author: David Markey , Citrix Systems. ## Licence: GNU LESSER GENERAL PUBLIC LICENSE V3, http://www.gnu.org/licenses/lgpl-3.0.txt ## THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN RISK! ## README before use. __version__ = "1.1.0" import os import tarfile import cStringIO import sys import copy ## Legacy Python 2.4 stuff try: import xml.etree.ElementTree as ET except: import elementtree.ElementTree as ET try: from hashlib import sha1 except: from sha import sha as sha1 try: from uuid import uuid1 as uuid except: ## RHEL/Python 2.4 is missing uuid, Dirty hack here import commands def uuid(): return commands.getoutput("uuidgen") os.SEEK_SET, os.SEEK_CUR, os.SEEK_END = range(3) ## XML BLOB !## XML_DATA=" version hostname cheesy-2 date 2009-12-02 product_version 5.5.0 product_brand XenServer build_number 24648p xapi_major 1 xapi_minor 3 export_vsn 2 objects class VM id Ref:0 snapshot uuid UUID1 allowed_operations export clone copy current_operations OpaqueRef:NULL export power_state Halted name_label ~Unnamed name_description user_version 1 is_a_template 0 suspend_VDI OpaqueRef:NULL resident_on OpaqueRef:NULL affinity OpaqueRef:b8c1cff1-2b1a-04c6-cf05-e8f832a0c369 memory_target 268435456 memory_static_max 268435456 memory_dynamic_max 268435456 memory_dynamic_min 268435456 memory_static_min 16777216 VCPUs_params VCPUs_max 1 VCPUs_at_startup 1 actions_after_shutdown destroy actions_after_reboot restart actions_after_crash restart consoles VIFs Ref:1 VBDs Ref:3 Ref:6 crash_dumps VTPMs PV_bootloader PV_kernel PV_ramdisk PV_args PV_bootloader_args PV_legacy_args HVM_boot_policy BIOS order HVM_boot_params order dc HVM_shadow_multiplier 1 platform nx false acpi true apic true pae true viridian true PCI_bus other_config install-methods cdrom mac_seed domid -1 domarch last_boot_CPU_flags is_control_domain 0 metrics OpaqueRef:NULL guest_metrics OpaqueRef:NULL last_booted_record recommendations <restrictions><restriction field="memory-static-max" max="34359738368" /><restriction field="vcpus-max" max="8" /><restriction property="number-of-vbds" max="7" /><restriction property="number-of-vifs" max="7" /></restrictions> xenstore_data ha_always_run 0 ha_restart_priority is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z transportable_snapshot_id blobs tags blocked_operations class VBD id Ref:6 snapshot uuid UUID2 allowed_operations attach eject current_operations VM Ref:0 VDI Ref:7 device userdevice 3 bootable 0 mode RO type CD unpluggable 1 storage_lock 0 empty 1 other_config currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL class VBD id Ref:3 snapshot uuid UUID3 allowed_operations attach current_operations VM Ref:0 VDI Ref:4 device xvda userdevice 0 bootable 1 mode RW type Disk unpluggable 0 storage_lock 0 empty 0 other_config owner true currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL class VIF id Ref:1 snapshot uuid UUID4 allowed_operations attach current_operations device 0 network Ref:2 VM Ref:0 MAC 00:00:00:00:00:00 MTU 0 other_config currently_attached 0 status_code 0 status_detail runtime_properties qos_algorithm_type qos_algorithm_params qos_supported_algorithms metrics OpaqueRef:NULL MAC_autogenerated 1 class network id Ref:2 snapshot uuid UUID5 name_label Pool-wide network associated with eth0 name_description allowed_operations current_operations VIFs Ref:1 PIFs other_config bridge xenbr0 blobs tags class VDI id Ref:7 snapshot uuid UUID6 name_label IDE 0.0 name_description allowed_operations current_operations SR Ref:8 VBDs Ref:6 crash_dumps virtual_size 4294965248 physical_utilisation 4294965248 type user sharable 0 read_only 1 other_config storage_lock 0 location /dev/xapi/cd/hda managed 1 missing 0 parent OpaqueRef:NULL xenstore_data sm_config hotplugged_at 2010-02-10T10:39:52Z is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z tags class VDI id Ref:4 snapshot uuid UUID7 name_label 0 name_description allowed_operations clone destroy resize current_operations SR Ref:5 VBDs Ref:3 crash_dumps virtual_size 5368709120 physical_utilisation 5385486336 type system sharable 0 read_only 0 other_config storage_lock 0 location ebe16ffc-7f5d-4761-9cfe-2f052577d64d managed 1 missing 0 parent OpaqueRef:NULL xenstore_data sm_config vdi_type vhd is_a_snapshot 0 snapshot_of OpaqueRef:NULL snapshots snapshot_time 19700101T00:00:00Z tags class SR id Ref:5 snapshot uuid UUID8 name_label Local storage name_description allowed_operations forget vdi_create vdi_snapshot plug update destroy vdi_destroy scan vdi_clone vdi_resize unplug current_operations VDIs Ref:4 PBDs virtual_allocation 39313211392 physical_utilisation 39325794304 physical_size 71777124352 type lvm content_type user shared 0 other_config i18n-original-value-name_label Local storage i18n-key local-storage tags sm_config allocation thick use_vhd true devserial scsi-SATA_WDC_WD800JD-75M_WD-WMAM9AAE1521 blobs class SR id Ref:8 snapshot uuid UUID9 name_label DVD drives name_description Physical DVD drives allowed_operations forget vdi_introduce plug update destroy scan vdi_clone unplug current_operations VDIs Ref:7 PBDs virtual_allocation 4294965248 physical_utilisation 4294965248 physical_size 4294965248 type udev content_type iso shared 0 other_config i18n-original-value-name_description Physical DVD drives i18n-original-value-name_label DVD drives i18n-key local-hotplug-cd tags sm_config type cd blobs " ## Taken from http://code.activestate.com/recipes/168639-progress-bar-class/ class ProgressBar: def __init__(self, min_value = 0, max_value = 100, width=77,**kwargs): self.char = kwargs.get('char', '#') self.mode = kwargs.get('mode', 'dynamic') # fixed or dynamic if not self.mode in ['fixed', 'dynamic']: self.mode = 'fixed' self.bar = '' self.min = min_value self.max = max_value self.span = max_value - min_value self.width = width self.amount = 0 # When amount == max, we are 100% done self.update_amount(0) def increment_amount(self, add_amount = 1): """ Increment self.amount by 'add_ammount' or default to incrementing by 1, and then rebuild the bar string. """ new_amount = self.amount + add_amount if new_amount < self.min: new_amount = self.min if new_amount > self.max: new_amount = self.max self.amount = new_amount self.build_bar() def update_amount(self, new_amount = None): """ Update self.amount with 'new_amount', and then rebuild the bar string. """ if not new_amount: new_amount = self.amount if new_amount < self.min: new_amount = self.min if new_amount > self.max: new_amount = self.max self.amount = new_amount self.build_bar() def build_bar(self): """ Figure new percent complete, and rebuild the bar string base on self.amount. """ diff = float(self.amount - self.min) percent_done = int(round((diff / float(self.span)) * 100.0)) # figure the proper number of 'character' make up the bar all_full = self.width - 2 num_hashes = int(round((percent_done * all_full) / 100)) if self.mode == 'dynamic': # build a progress bar with self.char (to create a dynamic bar # where the percent string moves along with the bar progress. self.bar = self.char * num_hashes else: # build a progress bar with self.char and spaces (to create a # fixe bar (the percent string doesn't move) self.bar = self.char * num_hashes + ' ' * (all_full-num_hashes) percent_str = str(percent_done) + "%" self.bar = '[ ' + self.bar + ' ] ' + percent_str def __str__(self): return str(self.bar) class Xva(object): classProgressBar = None def __init__(self, memory="268435456", vcpus=1, name="Unnamed", classProgressBar=None): self.classProgressBar = classProgressBar self.disks = [] self.vifs = [] self.nextref = 9 self.next_disk_letter = 1 raw_xml = XML_DATA for olduuid in [ 'UUID1', 'UUID2', 'UUID3', 'UUID4', 'UUID5', 'UUID6' , 'UUID7', 'UUID8' ,'UUID9' ]: raw_xml = raw_xml.replace(olduuid, str(uuid())) self.tree = ET.fromstring(raw_xml) xml_objects = {} config_struct = xml_objects['config_struct'] = self.tree.findall("struct/member")[1].find("value")[0][0][0][0][2][1][0] config_members = xml_objects['config_members'] = config_struct.findall("member") xml_objects['VIFS_config'] = config_members[23].find("value").findall("array") xml_objects['VBD_config'] = config_members[24].find("value").findall("array") xml_objects['PV_bootloader'] = config_members[27].find("value") xml_objects['PV_kernel'] = config_members[28].find("value") xml_objects['PV_ramdisk'] = config_members[29].find("value") xml_objects['PV_args'] = config_members[30].find("value") xml_objects['memory_static_max'] = config_members[12].find("value") xml_objects['memory_dynamic_max'] = config_members[13].find("value") xml_objects['memory_dynamic_min'] = config_members[14].find("value") xml_objects['vcpus'] = config_members[18].find("value") xml_objects['vcpus_max'] = config_members[17].find("value") xml_objects['HVM_boot_policy'] = config_members[33].find("value") xml_objects['nx'] = config_members[36][1][0][0][1].text xml_objects['acpi'] = config_members[36][1][0][1][1].text xml_objects['apic'] = config_members[36][1][0][2][1].text xml_objects['pae'] = config_members[36][1][0][3][1].text xml_objects['viridian'] = config_members[36][1][0][4][1].text xml_objects['name'] = config_members[4].find("value") xml_objects['first_vbd'] = self.tree.findall("struct/member")[1][1][0][0][2] xml_objects['first_vif'] = self.tree.findall("struct/member")[1][1][0][0][3][0] xml_objects['first_vdi'] = self.tree.findall("struct/member")[1][1][0][0][6] xml_objects['objects_array'] = self.tree.findall("struct/member")[1][1][0][0] self.xml_objects = xml_objects self.set_memory(memory) self.set_vcpus(vcpus) self.set_name(name) def new_ref(self): tmp = self.nextref self.nextref = self.nextref + 1 return "Ref:%d" % tmp def set_nx(self, value): if value: self.xml_objects['nx'] = "true" else: self.xml_objects['nx'] = "false" def set_acpi(self, value): if value: self.xml_objects['acpi'] = "true" else: self.xml_objects['acpi'] = "false" def set_apic(self, value): if value: self.xml_objects['apic'] = "true" else: self.xml_objects['apic'] = "false" def set_pae(self, value): if value: self.xml_objects['pae'] = "true" else: self.xml_objects['pae'] = "false" def set_viridian(self, value): if value: self.xml_objects['viridian'] = "true" else: self.xml_objects['viridian'] = "false" def print_report(self): print "VM Details:" print "Name: %s" % self.xml_objects['name'].text if self.xml_objects['HVM_boot_policy'].text == "BIOS order": print "Type: HVM" hvm=True else: print "Type: Paravirtualised" hvm=False print "VCPUS: %s" % self.xml_objects['vcpus'].text print "Memory(bytes): %s" % self.xml_objects['memory_static_max'].text print "ACPI: %s" % self.xml_objects['acpi'] print "APIC: %s" % self.xml_objects['apic'] print "PAE: %s" % self.xml_objects['pae'] print "NX: %s" % self.xml_objects['nx'] print "Viridian: %s" % self.xml_objects['viridian'] iteration = 0 for disk in self.disks: if iteration == 0: if hvm: print "Disk 0(Bootable): %s" % disk[1] else: print "Disk xvda(Bootable): %s" % disk[1] else: if hvm: print "Disk %d: %s" % ( iteration , disk[1]) else: print "Disk xvd%c: %s" % ( iteration + 97, disk[1]) iteration = iteration + 1 def add_disk(self, path): input_file = open(path, "rb") input_file.seek(0,os.SEEK_END) size = input_file.tell() if len(self.disks) == 0: self.xml_objects['first_vdi'][0][2][1][0][8][1].text = str(size) self.xml_objects['first_vdi'][0][2][1][0][9][1].text = str(size) ref = self.xml_objects['first_vdi'][0][1][1].text disk = (ref, path , size) self.disks.append(disk) else: ## copy a new vbd new_vbd_ref = self.new_ref() new_vdi_ref = self.new_ref() ## copy a new vbd new_vbd = copy.deepcopy(self.xml_objects['first_vbd']) ## Ref new_vbd[0][1][1].text = new_vbd_ref ## UUID new_vbd[0][2][1][0][0][1].text = str(uuid()) ## Map the VDI ref new_vbd[0][2][1][0][4][1].text = new_vdi_ref ## Set disk letter and userdevice new_vbd[0][2][1][0][5][1].text = "xvd%s" % chr(self.next_disk_letter + 97) new_vbd[0][2][1][0][6][1].text = str(self.next_disk_letter) ## bootable to false new_vbd[0][2][1][0][7][1][0].text = "0" ## copy a new vdi new_vdi = copy.deepcopy(self.xml_objects['first_vdi']) ## map the VBD ref new_vdi[0][2][1][0][6][1][0][0][0].text = new_vbd_ref ## uuid new_vdi[0][2][1][0][0][1].text = str(uuid()) ## ref new_vdi[0][1][1].text = new_vdi_ref new_vdi[0][2][1][0][8][1].text = str(size) new_vdi[0][2][1][0][9][1].text = str(size) ## name label new_vdi[0][2][1][0][1][1].text = str(self.next_disk_letter) disk = (new_vdi_ref, path , size) self.disks.insert(len(self.disks) -1,disk) self.xml_objects['objects_array'].append(new_vbd) self.xml_objects['objects_array'].append(new_vdi) new_vbd_value = copy.deepcopy(self.xml_objects['VBD_config'][0][0][0]) new_vbd_value.text = new_vbd_ref self.xml_objects['VBD_config'][0][0].append(new_vbd_value) self.next_disk_letter = self.next_disk_letter + 1 if self.next_disk_letter == 3: self.next_disk_letter = self.next_disk_letter + 1 def is_hvm(self): self.xml_objects['HVM_boot_policy'].text = "BIOS order" def is_pv(self): self.xml_objects['HVM_boot_policy'].text = "" self.xml_objects['PV_bootloader'].text = "pygrub" def set_name(self, name): self.xml_objects['name'].text = name def get_name(self): return self.xml_objects['name'].text def set_memory(self, memory): self.xml_objects['memory_static_max'].text = str(memory) self.xml_objects['memory_dynamic_max'].text = str(memory) self.xml_objects['memory_dynamic_min'].text = str(memory) def get_memory(self): return self.xml_objects['memory_static_max'].text def set_name(self, name): self.xml_objects['name'].text = name def get_name(self): return self.xml_objects['name'].text def set_vcpus(self, vcpus): self.xml_objects['vcpus'].text = str(vcpus) self.xml_objects['vcpus_max'].text = str(vcpus) def get_vcpus(self): return self.xml_objects['vcpus'].text def handle_exception(self): ## this is for when the http connection drops, we try to figure out what happened. if self.conn: try: response = self.conn.getresponse() except: print "Internal Error. Possible problem: Please make sure you have enough space on your default SR" sys.exit(254) if response.status == 401: print "Unauthorised response from server. Exiting" sys.exit(254) elif response.status != 200: print "Server returned error code %d. Exiting" % response.status print "Extra Info: %s" % response.read() sys.exit(254) else: print "Error writing file. Exiting" sys.exit(254) def save_as(self, filename=None, sparse=False, username=None, password=None, server=None, ssl=True): self.print_report() if server: print "Connecting to target %s" % server import base64 auth = base64.encodestring("%s:%s" % (username, password)).strip() import httplib if ssl: conn = httplib.HTTPSConnection(server) else: conn = httplib.HTTPConnection(server) headers = {"Authorization" : "Basic %s" % auth} conn.request("PUT", "/import", headers=headers) conn.write = conn.send self.conn = conn output_file = tarfile.open(fileobj=conn, mode='w|') else: output_file = tarfile.open(filename, mode='w|') print "Generating XVA file %s" % filename info = tarfile.TarInfo(name="ova.xml") output_xml = ET.tostring(self.tree) string = cStringIO.StringIO(output_xml) string.seek(0) info.size=len(output_xml) try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() chunksize=1048576 for disk in self.disks: basefilename=0 input_file = open(disk[1], "rb") input_file.seek(0,os.SEEK_END) input_file_size=input_file.tell() input_file.seek(0) position = 0 print "\nProcessing disk %s(%s bytes)" % (disk[1], input_file_size) read_len = -1 if self.classProgressBar == None: prog = ProgressBar(0, input_file_size, 77, mode='fixed') else: prog = self.classProgressBar oldprog = str(prog) while True: input_buffer = input_file.read(chunksize) read_len = len(input_buffer) if read_len == 0 : break force = False if position == 0: force=True if (input_file_size - position) < (chunksize * 2) : force = True position = position + chunksize if self.classProgressBar == None: prog.update_amount(position) else: prog.update_amount(float(position)/float(input_file_size)) if oldprog != str(prog): print prog, "\r", sys.stdout.flush() oldprog=str(prog) input_file.seek(position) zeroes = input_buffer.count('\0') if zeroes == chunksize and not force and sparse: basefilename = basefilename + 1 else: string = cStringIO.StringIO(input_buffer) string.seek(0) info = tarfile.TarInfo(name="%s/%08d" % (disk[0] , basefilename)) info.size=read_len try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() hash = sha1(input_buffer).hexdigest() string = cStringIO.StringIO(hash) info = tarfile.TarInfo(name="%s/%08d.checksum" % (disk[0], basefilename)) info.size=40 try: output_file.addfile(tarinfo=info, fileobj=string) except: self.handle_exception() basefilename = basefilename + 1 print "\n" sys.stdout.flush() output_file.close() if self.classProgressBar: prog.finish() if server: response = conn.getresponse() if response.status == 200: if self.classProgressBar == None: print "VM Successfully streamed" else: prog.update_text("VM Successfully streamed") else: if self.classProgressBar == None: print "VM did not stream successfully, Return code: %d" % response.status else: prog.update_text("VM did not stream successfully, Return code: %d" % response.status) if __name__ == "__main__": from optparse import OptionParser from optparse import OptionGroup parser = OptionParser() parser.add_option("-c", "--config", dest="config",default=None, help="Specify the OSS Xen config file to process(all other options except -o are ignored)", metavar="FILE") parser.add_option("--sparse", action="store_true", dest="sparse", help="Attempt sparse mode(detecting chunks that are zero)", default=False) params = OptionGroup(parser, "Virtual Machine Parameters", "These options are only read when you dont specify a config file with -c") params.add_option("-d", "--disk", action="append", dest="disks", help="Add disk in file/block device DISK, make sure first disk given is the boot disk", metavar="DISK") params.add_option("-m", "--memory", dest="memory", default=256, type="int", help="Set memory to MEM(Megabytes), default 256", metavar="MEM") params.add_option("-n", "--name", dest="name", default="Unnamed", help="Set VM name to NAME(default unnamed)", metavar="NAME") params.add_option("-v", "--vcpus", dest="vcpus", default=1, type="int", help="Set VCPUS to NUM(default 1)", metavar="NUM") params.add_option("--no-acpi", action="store_true", dest="noacpi", help="ACPI Disabled", default=False) params.add_option("--no-apic", action="store_true", dest="noapic", help="APIC Disabled", default=False) params.add_option("--no-viridian", action="store_true", dest="noviridian", help="Viridian Disabled", default=False) params.add_option("--no-pae", action="store_true", dest="nopae", help="PAE Disabled", default=False) params.add_option("--nx", action="store_true", dest="nx", help="NX enabled(default no)", default=False) params.add_option("--is-hvm", action="store_true", dest="hvm", help="Is HVM VM(defaults to HVM)", default=True) params.add_option("--is-pv", action="store_false", dest="hvm", help="Is PV VM") parser.add_option_group(params) output_options = OptionGroup(parser, "Output Options", "These are the options that dictates where the VM should be saved or streamed to a server. You can either save to a file or stream to a server, not both. " "One of either -o or -s have to be specified") output_options.add_option("-f", "--filename", dest="filename", help="Save XVA to file FILE", metavar="FILE", default=None) output_options.add_option("-s", "--server", dest="server", help="Stream VM to host HOSTNAME", metavar="HOSTNAME", default=None) output_options.add_option("--username", dest="username", help="Use username USERNAME when streaming to remote host", metavar="USERNAME") output_options.add_option("--password", dest="password", help="Use password PASSWORD when streaming to remote host", metavar="PASSWORD") output_options.add_option("--no-ssl", action="store_true", dest="nossl", help="SSL disabled with streaming", default=False) parser.add_option_group(output_options) (options, args) = parser.parse_args() if not options.server and not options.filename: parser.error("Please specify either a filename or server") if options.server and (not options.username or not options.password): parser.error("Please specify a username and password when streaming") machine = Xva() if options.config: params = {} execfile(options.config,params) if params.has_key("name"): machine.set_name( params['name'] ) if params.has_key("vcpus"): machine.set_vcpus( params['vcpus'] ) if params.has_key('kernel'): if params['kernel'].endswith("hvmloader"): machine.is_hvm() else: print "Kernels that are loaded from the Dom0 aren't supported. Use pygrub" sys.exit(255) else: machine.is_pv() if params.has_key("disk") and len(params['disk']) != 0: for disk in params['disk']: (path, device, mode) = disk.split(",") path_split = path.split(":") path_split.reverse() machine.add_disk(path_split[0]) else: print "You need at least 1 Disk, Exiting" sys.exit(254) if params.has_key("memory"): try: memory = int(params['memory'] ) machine.set_memory( memory * 1024 * 1024) except: print "Could parse memory, setting to 256M" machine.set_memory(268435456) if params.has_key("apic") and params['apic'] == 0: machine.set_apic(False) if params.has_key("acpi") and params['acpi'] == 0: machine.set_acpi(False) if params.has_key("nx") and params['nx'] == 1: machine.set_nx(options.nx) if params.has_key("pae") and params['pae'] == 0: machine.set_pae(False) else: if options.disks: for disk in options.disks: machine.add_disk(disk) else: parser.error("At least one disk needs to be specified") if options.hvm: machine.is_hvm() else: machine.is_pv() machine.set_name(options.name) machine.set_vcpus(options.vcpus) machine.set_acpi(not options.noacpi) machine.set_apic(not options.noapic) machine.set_nx(options.nx) machine.set_viridian(not options.noviridian) machine.set_pae(not options.nopae) memory = (options.memory * 1024 * 1024) machine.set_memory(memory) if options.filename: machine.save_as(filename=options.filename, sparse=options.sparse) else: machine.save_as(server=options.server, username=options.username, password=options.password, ssl= not options.nossl, sparse=options.sparse)