def build_widget(self, **kwargs): def remove_p(charm_class): n = self.pc.assignment_machine_count_for_charm(charm_class) return n > 0 def not_conflicted_p(cc): state, _, _ = self.pc.get_charm_state(cc) return state != CharmState.CONFLICTED actions = [(remove_p, 'Remove', self.do_remove), (not_conflicted_p, 'Add', self.do_add)] self.unrequired_undeployed_sl = ServicesList(self.pc, actions, actions, ignore_deployed=True, title="Un-Deployed") self.deployed_sl = ServicesList(self.pc, actions, actions, deployed_only=True, show_placements=True, title="Deployed Services") self.assigned_sl = ServicesList(self.pc, actions, actions, assigned_only=True, show_placements=True, title="Services to be Deployed") self.buttons = [] self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'center') self.pile1 = Pile([self.button_grid, self.assigned_sl, self.unrequired_undeployed_sl]) self.pile2 = Pile([self.deployed_sl]) return LineBox(Columns([self.pile1, self.pile2]), title="Add Services")
def build_widgets(self): self.services_list = ServicesList(self.placement_controller, self.display_controller, title=None) self.services_pile = Pile([self.services_list, Divider()]) return self.services_pile
def build_widgets(self): instructions = Text("Remove services from {}".format(self.machine.hostname)) self.machine_widget = MachineWidget(self.machine, self.controller, show_hardware=True) def show_remove_p(cc): md = self.controller.get_assignments(cc) for atype, ms in md.items(): hostnames = [m.hostname for m in ms] if self.machine.hostname in hostnames: return True return False actions = [(show_remove_p, "Remove", self.do_remove)] self.services_list = ServicesList(self.controller, actions, actions, machine=self.machine) close_button = AttrMap(Button("X", on_press=self.close_pressed), "button_secondary", "button_secondary focus") p = Pile( [ GridFlow([close_button], 5, 1, 0, "right"), instructions, Divider(), self.machine_widget, Divider(), self.services_list, ] ) return LineBox(p, title="Remove Services")
class ServicesColumn(WidgetWrap): """Displays dynamic list of unplaced services and associated controls """ def __init__(self, display_controller, placement_controller, placement_view): self.display_controller = display_controller self.placement_controller = placement_controller self.placement_view = placement_view w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.services_list = ServicesList(self.placement_controller, self.display_controller, title=None) self.services_pile = Pile([self.services_list, Divider()]) return self.services_pile def focus_top(self): self.update() self.services_list.focus_top() def focus_next(self): self.update() moved = self.services_list.focus_top_or_next() fsw = self.services_list.focused_service_widget() if not moved or (fsw and fsw.service.subordinate): self.placement_view.focus_footer() def update(self): self.services_list.update() def do_reset_to_defaults(self, sender): self.placement_controller.set_all_assignments( self.placement_controller.gen_defaults()) def clear_selections(self): for sw in self.services_list.service_widgets: sw.state = ServiceWidgetState.UNSELECTED def select_service(self, service_name): self.services_list.select_service(service_name)
def build_widgets(self): self.deploy_view = DeployView(self.display_controller, self.placement_controller, self.placement_view) def not_conflicted_p(cc): state, _, _ = self.placement_controller.get_charm_state(cc) return state != CharmState.CONFLICTED actions = [(not_conflicted_p, "Choose Machine", self.placement_view.do_show_machine_chooser)] subordinate_actions = [(not_conflicted_p, "Add", self.do_place_subordinate)] self.required_services_list = ServicesList(self.placement_controller, actions, subordinate_actions, ignore_assigned=True, ignore_deployed=True, show_type='required', show_constraints=True, title="Required Services") self.additional_services_list = ServicesList(self.placement_controller, actions, subordinate_actions, ignore_assigned=True, show_type='non-required', show_constraints=True, title="Additional " "Services") autoplace_func = self.placement_view.do_autoplace self.autoplace_button = AttrMap(Button("Auto-place Remaining Services", on_press=autoplace_func), 'button_secondary', 'button_secondary focus') clear_all_func = self.placement_view.do_clear_all self.clear_all_button = AttrMap(Button("Clear All Placements", on_press=clear_all_func), 'button_secondary', 'button_secondary focus') self.required_services_pile = Pile([self.required_services_list, Divider()]) self.additional_services_pile = Pile([self.additional_services_list, Divider()]) self.top_buttons = [] self.top_button_grid = GridFlow(self.top_buttons, 36, 1, 0, 'center') pl = [ Text(("body", "Services"), align='center'), Divider(), self.top_button_grid, Divider(), self.deploy_view, Divider(), self.required_services_pile, Divider(), self.additional_services_pile ] self.main_pile = Pile(pl) return self.main_pile
class ServicesColumn(WidgetWrap): """Displays dynamic list of unplaced services and associated controls """ def __init__(self, display_controller, placement_controller, placement_view): self.display_controller = display_controller self.placement_controller = placement_controller self.placement_view = placement_view w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): self.deploy_view = DeployView(self.display_controller, self.placement_controller, self.placement_view) def not_conflicted_p(cc): state, _, _ = self.placement_controller.get_charm_state(cc) return state != CharmState.CONFLICTED actions = [(not_conflicted_p, "Choose Machine", self.placement_view.do_show_machine_chooser)] subordinate_actions = [(not_conflicted_p, "Add", self.do_place_subordinate)] self.required_services_list = ServicesList(self.placement_controller, actions, subordinate_actions, ignore_assigned=True, ignore_deployed=True, show_type='required', show_constraints=True, title="Required Services") self.additional_services_list = ServicesList(self.placement_controller, actions, subordinate_actions, ignore_assigned=True, show_type='non-required', show_constraints=True, title="Additional " "Services") autoplace_func = self.placement_view.do_autoplace self.autoplace_button = AttrMap(Button("Auto-place Remaining Services", on_press=autoplace_func), 'button_secondary', 'button_secondary focus') clear_all_func = self.placement_view.do_clear_all self.clear_all_button = AttrMap(Button("Clear All Placements", on_press=clear_all_func), 'button_secondary', 'button_secondary focus') self.required_services_pile = Pile([self.required_services_list, Divider()]) self.additional_services_pile = Pile([self.additional_services_list, Divider()]) self.top_buttons = [] self.top_button_grid = GridFlow(self.top_buttons, 36, 1, 0, 'center') pl = [ Text(("body", "Services"), align='center'), Divider(), self.top_button_grid, Divider(), self.deploy_view, Divider(), self.required_services_pile, Divider(), self.additional_services_pile ] self.main_pile = Pile(pl) return self.main_pile def update(self): self.deploy_view.update() self.required_services_list.update() self.additional_services_list.update() top_buttons = [] unplaced = self.placement_controller.unassigned_undeployed_services() if len(unplaced) == 0: icon = SelectableIcon(" (Auto-place Remaining Services) ") top_buttons.append((AttrMap(icon, 'disabled_button', 'disabled_button_focus'), self.top_button_grid.options())) else: top_buttons.append((self.autoplace_button, self.top_button_grid.options())) top_buttons.append((self.clear_all_button, self.top_button_grid.options())) self.top_button_grid.contents = top_buttons def do_reset_to_defaults(self, sender): self.placement_controller.set_all_assignments( self.placement_controller.gen_defaults()) def do_place_subordinate(self, sender, charm_class): sub_placeholder = self.placement_controller.sub_placeholder self.placement_controller.assign(sub_placeholder, charm_class, AssignmentType.BareMetal)
class AddServicesDialog(WidgetWrap): """ Dialog to add services. Does not specify placement. :param cb: callback routine to process submit/cancel actions """ def __init__(self, install_controller, deploy_cb, cancel_cb): self.install_controller = install_controller self.orig_pc = install_controller.placement_controller self.pc = self.orig_pc.get_temp_copy() self.charms = [] self.deploy_cb = deploy_cb self.cancel_cb = cancel_cb self.boxes = [] w = self.build_widget() self.update() super().__init__(w) def build_widget(self, **kwargs): def remove_p(charm_class): n = self.pc.assignment_machine_count_for_charm(charm_class) return n > 0 def not_conflicted_p(cc): state, _, _ = self.pc.get_charm_state(cc) return state != CharmState.CONFLICTED actions = [(remove_p, 'Remove', self.do_remove), (not_conflicted_p, 'Add', self.do_add)] self.unrequired_undeployed_sl = ServicesList(self.pc, actions, actions, ignore_deployed=True, title="Un-Deployed") self.deployed_sl = ServicesList(self.pc, actions, actions, deployed_only=True, show_placements=True, title="Deployed Services") self.assigned_sl = ServicesList(self.pc, actions, actions, assigned_only=True, show_placements=True, title="Services to be Deployed") self.buttons = [] self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'center') self.pile1 = Pile([self.button_grid, self.assigned_sl, self.unrequired_undeployed_sl]) self.pile2 = Pile([self.deployed_sl]) return LineBox(Columns([self.pile1, self.pile2]), title="Add Services") def update(self): self.unrequired_undeployed_sl.update() self.deployed_sl.update() self.assigned_sl.update() self.update_buttons() def update_buttons(self): buttons = [(AttrMap(Button("Cancel", self.handle_cancel), 'button_primary', 'button_primary focus'), self.button_grid.options())] n_assigned = len(self.pc.assigned_services) if n_assigned > 0 and self.pc.can_deploy(): b = AttrMap(Button("Deploy", self.handle_deploy), 'button_primary', 'button_primary focus') else: b = AttrMap(SelectableIcon("(Deploy)"), 'disabled_button', 'disabled_button_focus') buttons.append((b, self.button_grid.options())) self.button_grid.contents = buttons def do_add(self, sender, charm_class): """Add the selected charm using default juju location. Equivalent to a simple 'juju deploy foo' Attempt to also add any charms that are required as a result of the newly added charm. """ def num_to_auto_add(charm_class): return (charm_class.required_num_units() - self.pc.assignment_machine_count_for_charm(charm_class)) for i in range(max(1, num_to_auto_add(charm_class))): self.pc.assign(self.pc.def_placeholder, charm_class, AssignmentType.DEFAULT) def get_unassigned_requireds(): return [cc for cc in self.pc.unassigned_undeployed_services() if self.pc.get_charm_state(cc)[0] == CharmState.REQUIRED] while len(get_unassigned_requireds()) > 0: for u_r_cc in get_unassigned_requireds(): for i in range(num_to_auto_add(u_r_cc)): self.pc.assign(self.pc.def_placeholder, u_r_cc, AssignmentType.DEFAULT) self.update() def do_remove(self, sender, charm_class): "Undo an assignment" self.pc.remove_one_assignment(self.pc.def_placeholder, charm_class) self.update() def handle_deploy(self, button): """Commits changes to the main placement controller, and calls the deploy callback to do the rest. """ self.orig_pc.update_from_controller(self.pc) self.deploy_cb() def handle_cancel(self, button): self.cancel_cb()
class ServiceChooser(WidgetWrap): """Presents list of services to put on a machine. Supports multiple selection, implying separate containers using --to. """ def __init__(self, controller, machine, parent_widget): """ controller is a PlacementController machine is the machine to show Services for parent_widget should support 'remove_overlay()' """ self.controller = controller self.machine = machine self.parent_widget = parent_widget w = self.build_widgets() super().__init__(w) def build_widgets(self): instructions = Text("Remove services from {}".format(self.machine.hostname)) self.machine_widget = MachineWidget(self.machine, self.controller, show_hardware=True) def show_remove_p(cc): md = self.controller.get_assignments(cc) for atype, ms in md.items(): hostnames = [m.hostname for m in ms] if self.machine.hostname in hostnames: return True return False actions = [(show_remove_p, "Remove", self.do_remove)] self.services_list = ServicesList(self.controller, actions, actions, machine=self.machine) close_button = AttrMap(Button("X", on_press=self.close_pressed), "button_secondary", "button_secondary focus") p = Pile( [ GridFlow([close_button], 5, 1, 0, "right"), instructions, Divider(), self.machine_widget, Divider(), self.services_list, ] ) return LineBox(p, title="Remove Services") def update(self): self.machine_widget.update() self.services_list.update() def do_add(self, sender, charm_class, atype): self.controller.assign(self.machine, charm_class, atype) self.update() def do_remove(self, sender, charm_class): self.controller.remove_one_assignment(self.machine, charm_class) self.update() def close_pressed(self, sender): self.parent_widget.remove_overlay(self)