def build_unselected_widgets(self): cdict = juju.constraints_to_dict(self.md.get('constraints', '')) self.juju_machine_id_button = MenuSelectButton( '{:20s}'.format(self.juju_machine_id), self.show_pin_chooser) self.juju_machine_id_label = Text("{:20s}".format( self.juju_machine_id)) self.cores_field = IntEdit('', str(cdict.get('cores', ''))) connect_signal(self.cores_field, 'change', self.handle_cores_changed) memval = cdict.get('mem', '') if memval != '': memval = memval / 1024 self.mem_field = Edit('', str(memval)) connect_signal(self.mem_field, 'change', self.handle_mem_changed) diskval = cdict.get('root-disk', '') if diskval != '': diskval = diskval / 1024 self.disk_field = Edit('', str(diskval)) connect_signal(self.disk_field, 'change', self.handle_disk_changed) if self.show_pin: machine_id_w = self.juju_machine_id_button else: machine_id_w = self.juju_machine_id_label cols = [ machine_id_w, self.cores_field, self.mem_field, self.disk_field ] cols = [AttrMap(w, 'string_input', 'string_input_focus') for w in cols] cols.append(Text("placeholder")) self.unselected_columns = Columns(cols, dividechars=2) self.update_assignments() return self.unselected_columns
def build_widgets(self): self.button = MenuSelectButton("I AM A MACHINE", self.do_select) self.action_button_cols = Columns([]) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile
def build_widgets(self): self.button = MenuSelectButton("I AM A SERVICE", self.do_select) self.action_button_cols = Columns([], dividechars=1) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile
class RelationWidget(WidgetWrap): def __init__(self, source_service_name, source_relname, interface, reltype, target_service_name, target_relname, placement_controller, select_cb): self.source_service_name = source_service_name self.source_relname = source_relname self.interface = interface self.reltype = reltype self.target_service_name = target_service_name self.target_relname = target_relname self.select_cb = select_cb self.placement_controller = placement_controller w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("", on_press=self.do_select) return AttrMap(self.button, 'text', 'button_secondary focus') def update(self): arrow = {RelationType.Provides: "\N{RIGHTWARDS ARROW}", RelationType.Requires: "\N{LEFTWARDS ARROW}"}[self.reltype] pc = self.placement_controller self.connected = pc.is_related(self.source_service_name, self.source_relname, self.target_service_name, self.target_relname) connstr = {True: "\N{CHECK MARK} ", False: " "}[self.connected] title = "{} {} {} {}:{}".format(connstr, self.source_relname, arrow, self.target_service_name, self.target_relname) self.button.set_label(title) def do_select(self, sender): self.select_cb(self.source_relname, self.target_service_name, self.target_relname)
def build_unselected_widgets(self): cdict = juju.constraints_to_dict(self.md.get('constraints', '')) self.juju_machine_id_button = MenuSelectButton( '{:20s}'.format(self.juju_machine_id), self.show_pin_chooser) self.juju_machine_id_label = Text( "{:20s}".format(self.juju_machine_id)) self.cores_field = IntEdit('', cdict.get('cores', '')) connect_signal(self.cores_field, 'change', self.handle_cores_changed) self.mem_field = Edit('', cdict.get('mem', '')) connect_signal(self.mem_field, 'change', self.handle_mem_changed) self.disk_field = Edit('', cdict.get('root-disk', '')) connect_signal(self.disk_field, 'change', self.handle_disk_changed) if self.show_pin: machine_id_w = self.juju_machine_id_button else: machine_id_w = self.juju_machine_id_label cols = [machine_id_w, self.cores_field, self.mem_field, self.disk_field] cols = [AttrMap(w, 'string_input', 'string_input_focus') for w in cols] cols.append(Text("placeholder")) self.unselected_columns = Columns(cols, dividechars=2) self.update_assignments() return self.unselected_columns
class RelationWidget(WidgetWrap): def __init__(self, source_service_name, source_relname, interface, reltype, target_service_name, target_relname, placement_controller, select_cb): self.source_service_name = source_service_name self.source_relname = source_relname self.interface = interface self.reltype = reltype self.target_service_name = target_service_name self.target_relname = target_relname self.select_cb = select_cb self.placement_controller = placement_controller w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("", on_press=self.do_select) return AttrMap(self.button, 'text', 'button_secondary focus') def update(self): arrow = { RelationType.Provides: "\N{RIGHTWARDS ARROW}", RelationType.Requires: "\N{LEFTWARDS ARROW}" }[self.reltype] pc = self.placement_controller self.connected = pc.is_related(self.source_service_name, self.source_relname, self.target_service_name, self.target_relname) connstr = {True: "\N{CHECK MARK} ", False: " "}[self.connected] title = "{} {} {} {}:{}".format(connstr, self.source_relname, arrow, self.target_service_name, self.target_relname) self.button.set_label(title) def do_select(self, sender): self.select_cb(self.source_relname, self.target_service_name, self.target_relname)
def build_widgets(self): self.services_column = ServicesColumn(self.display_controller, self.placement_controller, self) self.machines_column = MachinesColumn(self.display_controller, self.placement_controller, self) self.relations_column = RelationsColumn(self.display_controller, self.placement_controller, self, self.metadata_controller) self.charmstore_column = CharmstoreColumn(self.display_controller, self.placement_controller, self, self.metadata_controller) self.options_column = OptionsColumn(self.display_controller, self.placement_controller, self, self.metadata_controller) self.machines_header = self.get_machines_header(self.machines_column) self.relations_header = self.get_relations_header() self.services_header = self.get_services_header() self.charmstore_header = self.get_charmstore_header( self.charmstore_column) self.options_header = self.get_options_header(self.options_column) cs = [self.services_header, self.charmstore_header] self.header_columns = Columns(cs, dividechars=2) self.columns = Columns([self.services_column, self.machines_column], dividechars=2) self.deploy_button = MenuSelectButton("\nCommit\n", on_press=self.do_deploy) self.deploy_button_label = Text("Some charms use default") self.placement_edit_body_pile = Pile([self.columns]) self.placement_edit_body = Filler(Padding( self.placement_edit_body_pile, align='center', width=('relative', 95)), valign='top') self.bundle_graph_text = Text("No graph to display yet.") self.bundle_graph_widget = Padding(self.bundle_graph_text, 'center', 'pack') b = AttrMap(self.deploy_button, 'frame_header', 'button_primary focus') self.footer_grid = GridFlow( [self.deploy_button_label, Padding(b, width=28, align='center')], 28, 1, 1, 'right') f = AttrMap(self.footer_grid, 'frame_footer', 'frame_footer') self.frame = Frame(header=Pile([self.header_columns, HR()]), body=self.placement_edit_body, footer=f) return self.frame
def __init__(self, relname, iface, reltype): other_reltype = { RelationType.Requires: RelationType.Provides, RelationType.Provides: RelationType.Requires }[reltype] self.source_relname = relname self.reltype = reltype s = "({}: nothing in bundle {} {})".format(relname, other_reltype.name.lower(), iface) super().__init__( AttrMap(MenuSelectButton(s), 'label', 'button_secondary focus'))
def build_widgets(self): self.charm_name = self.md['Name'] summary = self.md['Summary'] top = [] pad = 0 if self.header: top = [Text("\n{}\n".format(self.header))] if self.recommended: pad = 2 s = "{} ({})\n {}\n".format(self.charm_name, self.charm_source, summary) b = MenuSelectButton(s, on_press=self.handle_press) return Pile( top + [AttrMap(Padding(b, left=pad), 'text', 'button_secondary focus')])
def build_widgets(self): name_with_rev = self.bundle_source.split("/")[-1] bundle_name = "-".join(name_with_rev.split("-")[:-1]) if 'Machines' in self.md: summary = ("{} services " "on {} machines").format(len(self.md['Services']), len(self.md['Machines'])) else: summary = "{} services".format(len(self.md['Services'])) s = "{} ({})\n {}\n".format(bundle_name, self.bundle_source, summary) top = [] if self.header: top = [Text("\n{}\n".format(self.header))] b = MenuSelectButton(s, on_press=self.handle_press) return Pile(top + [AttrMap(b, 'text', 'button_secondary focus')])
class JujuMachineWidget(WidgetWrap): """A widget displaying a machine and action buttons. juju_machine_id = juju id of the machine md = the machine dict application - the current application for which machines are being shown assign_cb - a function that takes a machine and assignmenttype to perform the button action unassign_cb - a function that takes a machine and removes assignments for the machine controller - a controller object that provides show_pin_chooser show_assignments - display info about which charms are assigned and what assignment type (LXC, KVM, etc) they have. show_pin - show button to pin juju machine to a maas machine """ def __init__(self, juju_machine_id, md, application, assign_cb, unassign_cb, controller, show_assignments=True, show_pin=False): self.juju_machine_id = juju_machine_id self.md = md self.application = application self.is_selected = False self.assign_cb = assign_cb self.unassign_cb = unassign_cb self.controller = controller self.show_assignments = show_assignments self.show_pin = show_pin self.all_assigned = False w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def __repr__(self): return "jujumachinewidget #" + str(self.juju_machine_id) def build_widgets(self): self.action_button_cols = Columns([]) self.action_buttons = [] self.build_unselected_widgets() self.pile = Pile([self.unselected_columns]) return self.pile def build_unselected_widgets(self): cdict = juju.constraints_to_dict(self.md.get('constraints', '')) self.juju_machine_id_button = MenuSelectButton( '{:20s}'.format(self.juju_machine_id), self.show_pin_chooser) self.juju_machine_id_label = Text("{:20s}".format( self.juju_machine_id)) self.cores_field = IntEdit('', str(cdict.get('cores', ''))) connect_signal(self.cores_field, 'change', self.handle_cores_changed) memval = cdict.get('mem', '') if memval != '': memval = memval / 1024 self.mem_field = Edit('', str(memval)) connect_signal(self.mem_field, 'change', self.handle_mem_changed) diskval = cdict.get('root-disk', '') if diskval != '': diskval = diskval / 1024 self.disk_field = Edit('', str(diskval)) connect_signal(self.disk_field, 'change', self.handle_disk_changed) if self.show_pin: machine_id_w = self.juju_machine_id_button else: machine_id_w = self.juju_machine_id_label cols = [ machine_id_w, self.cores_field, self.mem_field, self.disk_field ] cols = [AttrMap(w, 'string_input', 'string_input_focus') for w in cols] cols.append(Text("placeholder")) self.unselected_columns = Columns(cols, dividechars=2) self.update_assignments() return self.unselected_columns def update_assignments(self): assignments = [] mps = self.controller.get_all_assignments(self.juju_machine_id) if len(mps) > 0: if self.show_assignments: ad = defaultdict(list) for application, atype in mps: ad[atype].append(application) astr = " ".join([ "{}{}".format( atype_to_label([atype])[0], ",".join( [application.service_name for application in al])) for atype, al in ad.items() ]) assignments.append(astr) else: if self.show_assignments: assignments.append("-") if any([application == self.application for application, _ in mps]): action = self.do_remove label = "Remove" else: action = self.do_select label = "Select" self.select_button = PlainButton(label, action) cols = [Text(s) for s in assignments] current_assignments = [a for a, _ in mps if a == self.application] if self.all_assigned and len(current_assignments) == 0: cols.append(Text("")) else: cols += [ AttrMap(self.select_button, 'text', 'button_secondary focus') ] opts = self.unselected_columns.options() self.unselected_columns.contents[4:] = [(w, opts) for w in cols] def update(self): self.update_action_buttons() if self.is_selected: self.update_selected() else: self.update_unselected() def update_selected(self): cn = self.application.service_name msg = Text(" Add {} to machine #{}:".format(cn, self.juju_machine_id)) self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_unselected(self): if self.show_pin: pinned_machine = self.controller.get_pin(self.juju_machine_id) if pinned_machine: pin_label = " {} \N{PENCIL}".format(pinned_machine.hostname) else: pin_label = " \N{PENCIL}" self.juju_machine_id_button.set_label( '{:20s}'.format(self.juju_machine_id + " " + pin_label)) else: self.juju_machine_id_label.set_text('{:20s}'.format( self.juju_machine_id)) self.pile.contents = [(self.unselected_columns, self.pile.options()), (Divider(), self.pile.options())] self.update_assignments() def update_action_buttons(self): all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.select_baremetal), (AssignmentType.LXD, 'Add as LXD', self.select_lxd), (AssignmentType.KVM, 'Add as KVM', self.select_kvm)] sc = self.application if sc: allowed_set = set(sc.allowed_assignment_types) allowed_types = set([atype for atype, _, _ in all_actions]) allowed_types = allowed_types.intersection(allowed_set) else: allowed_types = set() # + 1 for the cancel button: if len(self.action_buttons) == len(allowed_types) + 1: return self.action_buttons = [ AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for atype, label, func in all_actions if atype in allowed_types ] self.action_buttons.append( AttrMap(PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): self.is_selected = True self.update() self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 def do_remove(self, sender): self.unassign_cb(self.juju_machine_id) def do_cancel(self, sender): self.is_selected = False self.update() self.pile.focus_position = 0 def _do_select_assignment(self, atype): self.assign_cb(self.juju_machine_id, atype) self.pile.focus_position = 0 self.is_selected = False self.update() def select_baremetal(self, sender): self._do_select_assignment(AssignmentType.BareMetal) def select_lxd(self, sender): self._do_select_assignment(AssignmentType.LXD) def select_kvm(self, sender): self._do_select_assignment(AssignmentType.KVM) def handle_cores_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'cores') else: self.md = self.controller.set_constraint(self.juju_machine_id, 'cores', val) def _format_constraint(self, val): "Store constraints as int values of megabytes" if val.isnumeric(): return int(val) * 1024 else: return units.human_to_mb(val) def handle_mem_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'mem') else: try: self.md = self.controller.set_constraint( self.juju_machine_id, 'mem', self._format_constraint(val)) except ValueError: return def handle_disk_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'root-disk') else: try: self.md = self.controller.set_constraint( self.juju_machine_id, 'root-disk', self._format_constraint(val)) except ValueError: return def show_pin_chooser(self, sender): self.controller.show_pin_chooser(self.juju_machine_id)
class SimpleServiceWidget(WidgetWrap): """A widget displaying a service as a button service - the service to display placement_controller - a PlacementController instance display_controller - a PlacerView instance callback - a function to be called when either of the buttons is pressed. The service will be passed to the function as userdata. show_placements - display the machine(s) currently assigned to host this service, both planned deployments (aka 'assignments', and already-deployed, called 'deployments'). """ def __init__(self, service, placement_controller, display_controller, show_placements=False): self.service = service self.placement_controller = placement_controller self.display_controller = display_controller self.show_placements = show_placements self.state = ServiceWidgetState.UNSELECTED w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("I AM A SERVICE", self.do_select) self.action_button_cols = Columns([], dividechars=1) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile def get_markup(self): if self.service.subordinate: return [self.service.service_name + " (subordinate)\n " + self.service.charm_source], [] nr = self.service.required_num_units() pl = "s" if nr > 1 else "" title_markup = [self.service.service_name + "\n {} unit{}: ".format(nr, pl) + self.service.charm_source] info_markup = [] if not self.display_controller.has_maas: return title_markup, info_markup pd = self.placement_controller.get_assignments(self.service) nplaced = sum([len(pd[k]) for k in pd]) if nr-nplaced > 0: pl = "" if nr-nplaced > 1: pl = "s" info_str = (" {} unit{} will be auto-placed " "by Juju\n".format(nr-nplaced, pl)) info_markup.append(info_str) def string_for_placement_dict(d): if self.display_controller.has_maas: defstring = "Bare Metal (Default)" else: defstring = "LXD (Default)" s = [] for atype, ml in sorted(d.items()): if atype == AssignmentType.DEFAULT: aname = defstring else: aname = atype.name hostnames = [m.hostname for m in ml] s.append(" {}: {}".format(aname, ", ".join(hostnames))) if len(s) == 0: return [] return "\n".join(s) ad = self.placement_controller.get_assignments(self.service) info_markup += string_for_placement_dict(ad) return title_markup, info_markup def update_choosing(self): title_markup, _ = self.get_markup() msg = Padding(Text(title_markup), left=2, right=2, align='center') self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_default(self): title_markup, info_markup = self.get_markup() self.button.set_label(title_markup + ["\n"] + info_markup) if self.state == ServiceWidgetState.SELECTED: b = AttrMap(self.button, 'deploy_highlight_start', 'button_secondary focus') else: b = AttrMap(self.button, 'text', 'button_secondary focus') self.pile.contents = [(b, self.pile.options()), (Divider(), self.pile.options())] def update(self): self.service = next((s for s in self.placement_controller.bundle.services if s.service_name == self.service.service_name), self.service) self.update_action_buttons() if self.state == ServiceWidgetState.CHOOSING: self.update_choosing() else: self.update_default() def keypress(self, size, key): if key == 'backspace': self.display_controller.remove_service(self.service) elif key == '+': if not self.service.subordinate: self.display_controller.scale_service(self.service, 1) elif key == '-': if not self.service.subordinate: self.display_controller.scale_service(self.service, -1) return super().keypress(size, key) def update_action_buttons(self): all_actions = [] if self.display_controller.has_maas: all_actions = [('Choose Placement', self.handle_placement_button_pressed)] all_actions += [('Edit Relations', self.handle_relation_button_pressed), ('Edit Options', self.handle_options_button_pressed)] self.action_buttons = [AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for label, func in all_actions] self.action_buttons.append(AttrMap( PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): self.display_controller.clear_selections() if self.state == ServiceWidgetState.SELECTED: self.state = ServiceWidgetState.UNSELECTED self.display_controller.set_selected_service(None) else: self.display_controller.set_selected_service(self.service) self.state = ServiceWidgetState.CHOOSING self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 self.update() def do_cancel(self, sender): self.state = ServiceWidgetState.UNSELECTED self.update() self.display_controller.show_default_view() self.pile.focus_position = 0 def handle_placement_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_placement() self.pile.focus_position = 0 def handle_relation_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_relations() self.pile.focus_position = 0 def handle_options_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_options() self.pile.focus_position = 0
def build_widgets(self): self.button = MenuSelectButton("", on_press=self.do_select) return AttrMap(self.button, 'text', 'button_secondary focus')
class SimpleServiceWidget(WidgetWrap): """A widget displaying a service as a button service - the service to display placement_controller - a PlacementController instance display_controller - a PlacerView instance callback - a function to be called when either of the buttons is pressed. The service will be passed to the function as userdata. show_placements - display the machine(s) currently assigned to host this service, both planned deployments (aka 'assignments', and already-deployed, called 'deployments'). """ def __init__(self, service, placement_controller, display_controller, show_placements=False): self.service = service self.placement_controller = placement_controller self.display_controller = display_controller self.show_placements = show_placements self.state = ServiceWidgetState.UNSELECTED w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("I AM A SERVICE", self.do_select) self.action_button_cols = Columns([], dividechars=1) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile def get_markup(self): if self.service.subordinate: return [self.service.service_name + " (subordinate)\n " + self.service.charm_source], [] nr = self.service.required_num_units() pl = "s" if nr > 1 else "" title_markup = [self.service.service_name + "\n {} unit{}: ".format(nr, pl) + self.service.charm_source] info_markup = [] if not self.display_controller.has_maas: return title_markup, info_markup pd = self.placement_controller.get_assignments(self.service) nplaced = sum([len(pd[k]) for k in pd]) if nr - nplaced > 0: pl = "" if nr - nplaced > 1: pl = "s" info_str = (" {} unit{} will be auto-placed " "by Juju\n".format(nr - nplaced, pl)) info_markup.append(info_str) def string_for_placement_dict(d): if self.display_controller.has_maas: defstring = "Bare Metal (Default)" else: defstring = "LXD (Default)" s = [] for atype, ml in sorted(d.items()): if atype == AssignmentType.DEFAULT: aname = defstring else: aname = atype.name hostnames = [m.hostname for m in ml] s.append(" {}: {}".format(aname, ", ".join(hostnames))) if len(s) == 0: return [] return "\n".join(s) ad = self.placement_controller.get_assignments(self.service) info_markup += string_for_placement_dict(ad) return title_markup, info_markup def update_choosing(self): title_markup, _ = self.get_markup() msg = Padding(Text(title_markup), left=2, right=2, align='center') self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_default(self): title_markup, info_markup = self.get_markup() self.button.set_label(title_markup + ["\n"] + info_markup) if self.state == ServiceWidgetState.SELECTED: b = AttrMap(self.button, 'deploy_highlight_start', 'button_secondary focus') else: b = AttrMap(self.button, 'text', 'button_secondary focus') self.pile.contents = [(b, self.pile.options()), (Divider(), self.pile.options())] def update(self): self.service = next((s for s in self.placement_controller.bundle.services if s.service_name == self.service.service_name), self.service) self.update_action_buttons() if self.state == ServiceWidgetState.CHOOSING: self.update_choosing() else: self.update_default() def keypress(self, size, key): if key == 'backspace': self.display_controller.remove_service(self.service) elif key == '+': if not self.service.subordinate: self.display_controller.scale_service(self.service, 1) elif key == '-': if not self.service.subordinate: self.display_controller.scale_service(self.service, -1) return super().keypress(size, key) def update_action_buttons(self): all_actions = [] if self.display_controller.has_maas: all_actions = [('Choose Placement', self.handle_placement_button_pressed)] all_actions += [('Edit Relations', self.handle_relation_button_pressed), ('Edit Options', self.handle_options_button_pressed)] self.action_buttons = [AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for label, func in all_actions] self.action_buttons.append(AttrMap( PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): self.display_controller.clear_selections() if self.state == ServiceWidgetState.SELECTED: self.state = ServiceWidgetState.UNSELECTED self.display_controller.set_selected_service(None) else: self.display_controller.set_selected_service(self.service) self.state = ServiceWidgetState.CHOOSING self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 self.update() def do_cancel(self, sender): self.state = ServiceWidgetState.UNSELECTED self.update() self.display_controller.show_default_view() self.pile.focus_position = 0 def handle_placement_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_placement() self.pile.focus_position = 0 def handle_relation_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_relations() self.pile.focus_position = 0 def handle_options_button_pressed(self, sender): self.state = ServiceWidgetState.SELECTED self.update() self.display_controller.edit_options() self.pile.focus_position = 0
class SimpleMachineWidget(WidgetWrap): """A widget displaying a machine. When selected, shows action buttons for placement types. machine - the machine to display controller - a PlacementController instance display_controller - a PlacerView instance show_assignments - display info about which charms are assigned and what assignment type (LXC, KVM, etc) they have. """ def __init__(self, machine, controller, display_controller, show_assignments=True): self.machine = machine self.controller = controller self.display_controller = display_controller self.show_assignments = show_assignments self.is_selected = False w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("I AM A MACHINE", self.do_select) self.action_button_cols = Columns([]) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile def update_machine(self): """Refresh with potentially updated machine info from controller. Assumes that machine exists - machines going away is handled in machineslist.update(). """ self.machine = next((m for m in self.controller.machines() if m.instance_id == self.machine.instance_id), None) def update(self): self.update_machine() self.update_action_buttons() if self.is_selected: self.update_selected() else: self.update_unselected() def info_markup(self): m = self.machine return [ self.machine.hostname + "\n", 'arch: {} '.format(m.arch), 'cores: {} '.format(m.cpu_cores), 'mem: {} '.format(m.mem), 'storage: {}'.format(m.storage) ] def update_selected(self): cn = self.display_controller.selected_service.service_name msg = Text(" Add {} to {}:".format(cn, self.machine.hostname)) self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_unselected(self): markup = self.info_markup() self.button.set_label(markup) self.pile.contents = [(AttrMap(self.button, 'text', 'button_secondary focus'), self.pile.options()), (Divider(), self.pile.options())] def update_action_buttons(self): all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.select_baremetal), (AssignmentType.LXD, 'Add as LXD', self.select_lxd), (AssignmentType.KVM, 'Add as KVM', self.select_kvm)] sc = self.display_controller.selected_service if sc: allowed_set = set(sc.allowed_assignment_types) allowed_types = set([atype for atype, _, _ in all_actions]) allowed_types = allowed_types.intersection(allowed_set) else: allowed_types = set() # + 1 for the cancel button: if len(self.action_buttons) == len(allowed_types) + 1: return self.action_buttons = [ AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for atype, label, func in all_actions if atype in allowed_types ] self.action_buttons.append( AttrMap(PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): if self.display_controller.selected_service is None: return self.is_selected = True self.update() self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 def do_cancel(self, sender): self.is_selected = False self.update() self.pile.focus_position = 0 def _do_select_assignment(self, atype): { AssignmentType.BareMetal: self.display_controller.do_select_baremetal, AssignmentType.LXD: self.display_controller.do_select_lxd, AssignmentType.KVM: self.display_controller.do_select_kvm }[atype](self.machine) self.pile.focus_position = 0 def select_baremetal(self, sender): self._do_select_assignment(AssignmentType.BareMetal) def select_lxd(self, sender): self._do_select_assignment(AssignmentType.LXD) def select_kvm(self, sender): self._do_select_assignment(AssignmentType.KVM)
class SimpleMachineWidget(WidgetWrap): """A widget displaying a machine. When selected, shows action buttons for placement types. machine - the machine to display controller - a PlacementController instance display_controller - a PlacerView instance show_assignments - display info about which charms are assigned and what assignment type (LXC, KVM, etc) they have. """ def __init__(self, machine, controller, display_controller, show_assignments=True): self.machine = machine self.controller = controller self.display_controller = display_controller self.show_assignments = show_assignments self.is_selected = False w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.button = MenuSelectButton("I AM A MACHINE", self.do_select) self.action_button_cols = Columns([]) self.action_buttons = [] self.pile = Pile([self.button]) return self.pile def update_machine(self): """Refresh with potentially updated machine info from controller. Assumes that machine exists - machines going away is handled in machineslist.update(). """ self.machine = next((m for m in self.controller.machines() if m.instance_id == self.machine.instance_id), None) def update(self): self.update_machine() self.update_action_buttons() if self.is_selected: self.update_selected() else: self.update_unselected() def info_markup(self): m = self.machine return [self.machine.hostname + "\n", 'arch: {} '.format(m.arch), 'cores: {} '.format(m.cpu_cores), 'mem: {} '.format(m.mem), 'storage: {}'.format(m.storage)] def update_selected(self): cn = self.display_controller.selected_service.service_name msg = Text(" Add {} to {}:".format(cn, self.machine.hostname)) self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_unselected(self): markup = self.info_markup() self.button.set_label(markup) self.pile.contents = [(AttrMap(self.button, 'text', 'button_secondary focus'), self.pile.options()), (Divider(), self.pile.options())] def update_action_buttons(self): all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.select_baremetal), (AssignmentType.LXD, 'Add as LXD', self.select_lxd), (AssignmentType.KVM, 'Add as KVM', self.select_kvm)] sc = self.display_controller.selected_service if sc: allowed_set = set(sc.allowed_assignment_types) allowed_types = set([atype for atype, _, _ in all_actions]) allowed_types = allowed_types.intersection(allowed_set) else: allowed_types = set() # + 1 for the cancel button: if len(self.action_buttons) == len(allowed_types) + 1: return self.action_buttons = [AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for atype, label, func in all_actions if atype in allowed_types] self.action_buttons.append( AttrMap(PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): if self.display_controller.selected_service is None: return self.is_selected = True self.update() self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 def do_cancel(self, sender): self.is_selected = False self.update() self.pile.focus_position = 0 def _do_select_assignment(self, atype): {AssignmentType.BareMetal: self.display_controller.do_select_baremetal, AssignmentType.LXD: self.display_controller.do_select_lxd, AssignmentType.KVM: self.display_controller.do_select_kvm}[atype](self.machine) self.pile.focus_position = 0 def select_baremetal(self, sender): self._do_select_assignment(AssignmentType.BareMetal) def select_lxd(self, sender): self._do_select_assignment(AssignmentType.LXD) def select_kvm(self, sender): self._do_select_assignment(AssignmentType.KVM)
class JujuMachineWidget(WidgetWrap): """A widget displaying a machine and action buttons. juju_machine_id = juju id of the machine md = the machine dict application - the current application for which machines are being shown assign_cb - a function that takes a machine and assignmenttype to perform the button action unassign_cb - a function that takes a machine and removes assignments for the machine controller - a controller object that provides show_pin_chooser show_assignments - display info about which charms are assigned and what assignment type (LXC, KVM, etc) they have. show_pin - show button to pin juju machine to a maas machine """ def __init__(self, juju_machine_id, md, application, assign_cb, unassign_cb, controller, show_assignments=True, show_pin=False): self.juju_machine_id = juju_machine_id self.md = md self.application = application self.is_selected = False self.assign_cb = assign_cb self.unassign_cb = unassign_cb self.controller = controller self.show_assignments = show_assignments self.show_pin = show_pin self.all_assigned = False w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def __repr__(self): return "jujumachinewidget #" + str(self.juju_machine_id) def build_widgets(self): self.action_button_cols = Columns([]) self.action_buttons = [] self.build_unselected_widgets() self.pile = Pile([self.unselected_columns]) return self.pile def build_unselected_widgets(self): cdict = juju.constraints_to_dict(self.md.get('constraints', '')) self.juju_machine_id_button = MenuSelectButton( '{:20s}'.format(self.juju_machine_id), self.show_pin_chooser) self.juju_machine_id_label = Text( "{:20s}".format(self.juju_machine_id)) self.cores_field = IntEdit('', cdict.get('cores', '')) connect_signal(self.cores_field, 'change', self.handle_cores_changed) self.mem_field = Edit('', cdict.get('mem', '')) connect_signal(self.mem_field, 'change', self.handle_mem_changed) self.disk_field = Edit('', cdict.get('root-disk', '')) connect_signal(self.disk_field, 'change', self.handle_disk_changed) if self.show_pin: machine_id_w = self.juju_machine_id_button else: machine_id_w = self.juju_machine_id_label cols = [machine_id_w, self.cores_field, self.mem_field, self.disk_field] cols = [AttrMap(w, 'string_input', 'string_input_focus') for w in cols] cols.append(Text("placeholder")) self.unselected_columns = Columns(cols, dividechars=2) self.update_assignments() return self.unselected_columns def update_assignments(self): assignments = [] mps = self.controller.get_all_assignments(self.juju_machine_id) if len(mps) > 0: if self.show_assignments: ad = defaultdict(list) for application, atype in mps: ad[atype].append(application) astr = " ".join(["{}{}".format( atype_to_label([atype])[0], ",".join([application.service_name for application in al])) for atype, al in ad.items()]) assignments.append(astr) else: if self.show_assignments: assignments.append("-") if any([application == self.application for application, _ in mps]): action = self.do_remove label = "Remove" else: action = self.do_select label = "Select" self.select_button = PlainButton(label, action) cols = [Text(s) for s in assignments] current_assignments = [a for a, _ in mps if a == self.application] if self.all_assigned and len(current_assignments) == 0: cols.append(Text("")) else: cols += [AttrMap(self.select_button, 'text', 'button_secondary focus')] opts = self.unselected_columns.options() self.unselected_columns.contents[4:] = [(w, opts) for w in cols] def update(self): self.update_action_buttons() if self.is_selected: self.update_selected() else: self.update_unselected() def update_selected(self): cn = self.application.service_name msg = Text(" Add {} to machine #{}:".format(cn, self.juju_machine_id)) self.pile.contents = [(msg, self.pile.options()), (self.action_button_cols, self.pile.options()), (Divider(), self.pile.options())] def update_unselected(self): if self.show_pin: pinned_machine = self.controller.get_pin(self.juju_machine_id) if pinned_machine: pin_label = " {} \N{PENCIL}".format(pinned_machine.hostname) else: pin_label = " \N{PENCIL}" self.juju_machine_id_button.set_label('{:20s}'.format( self.juju_machine_id + " " + pin_label)) else: self.juju_machine_id_label.set_text('{:20s}'.format( self.juju_machine_id)) self.pile.contents = [(self.unselected_columns, self.pile.options()), (Divider(), self.pile.options())] self.update_assignments() def update_action_buttons(self): all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.select_baremetal), (AssignmentType.LXD, 'Add as LXD', self.select_lxd), (AssignmentType.KVM, 'Add as KVM', self.select_kvm)] sc = self.application if sc: allowed_set = set(sc.allowed_assignment_types) allowed_types = set([atype for atype, _, _ in all_actions]) allowed_types = allowed_types.intersection(allowed_set) else: allowed_types = set() # + 1 for the cancel button: if len(self.action_buttons) == len(allowed_types) + 1: return self.action_buttons = [AttrMap(PlainButton(label, on_press=func), 'button_secondary', 'button_secondary focus') for atype, label, func in all_actions if atype in allowed_types] self.action_buttons.append( AttrMap(PlainButton("Cancel", on_press=self.do_cancel), 'button_secondary', 'button_secondary focus')) opts = self.action_button_cols.options() self.action_button_cols.contents = [(b, opts) for b in self.action_buttons] def do_select(self, sender): self.is_selected = True self.update() self.pile.focus_position = 1 self.action_button_cols.focus_position = 0 def do_remove(self, sender): self.unassign_cb(self.juju_machine_id) def do_cancel(self, sender): self.is_selected = False self.update() self.pile.focus_position = 0 def _do_select_assignment(self, atype): self.assign_cb(self.juju_machine_id, atype) self.pile.focus_position = 0 self.is_selected = False self.update() def select_baremetal(self, sender): self._do_select_assignment(AssignmentType.BareMetal) def select_lxd(self, sender): self._do_select_assignment(AssignmentType.LXD) def select_kvm(self, sender): self._do_select_assignment(AssignmentType.KVM) def handle_cores_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'cores') else: self.md = self.controller.set_constraint(self.juju_machine_id, 'cores', val) def _format_constraint(self, val): """Ensure that a constraint has a unit. bare numbers are treated as gigabytes""" try: return units.gb_to_human(float(val)) except ValueError: return val def handle_mem_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'mem') else: self.md = self.controller.set_constraint( self.juju_machine_id, 'mem', self._format_constraint(val)) def handle_disk_changed(self, sender, val): if val == '': self.md = self.controller.clear_constraint(self.juju_machine_id, 'root-disk') else: self.md = self.controller.set_constraint( self.juju_machine_id, 'root-disk', self._format_constraint(val)) def show_pin_chooser(self, sender): self.controller.show_pin_chooser(self.juju_machine_id)