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(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()
Ejemplo n.º 2
0
class MachineWidget(WidgetWrap):
    """A widget displaying a service and associated actions.

    machine - the machine to display

    controller - a PlacementController instance

    actions - a list of ('label', function) pairs that wil be used to
    create buttons for each machine.  The machine will be passed to
    the function as userdata.

    optionally, actions can be a 3-tuple (pred, 'label', function),
    where pred determines whether to add the button. Pred will be
    passed the charm class.

    show_hardware - display hardware details about this machine

    show_assignments - display info about which charms are assigned
    and what assignment type (LXC, KVM, etc) they have.
    """
    def __init__(self,
                 machine,
                 controller,
                 actions=None,
                 show_hardware=False,
                 show_assignments=True):
        self.machine = machine
        self.controller = controller
        if actions is None:
            self.actions = []
        else:
            self.actions = actions
        self.show_hardware = show_hardware
        self.show_assignments = show_assignments
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        return True

    def hardware_info_markup(self):
        m = self.machine
        return [('label', 'arch'), ' {}  '.format(m.arch),
                ('label', 'cores'), ' {}  '.format(m.cpu_cores),
                ('label', 'mem'), ' {}  '.format(m.mem), ('label', 'storage'),
                ' {}'.format(m.storage)]

    def build_widgets(self):

        self.machine_info_widget = Text("")
        self.assignments_widget = Text("")
        self.hardware_widget = Text("")

        self.buttons = []
        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'right')

        pl = [Divider(' '), self.machine_info_widget]
        if self.show_hardware:
            pl.append(self.hardware_widget)
        if self.show_assignments:
            pl += [Divider(' '), self.assignments_widget]
        pl.append(self.button_grid)

        p = Pile(pl)

        return Padding(p, left=2, right=2)

    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()
        if self.machine == self.controller.sub_placeholder:
            self.machine_info_widget.set_text("\N{BULLET} Subordinate Charms")
            self.hardware_widget.set_text("")
        elif self.machine == self.controller.def_placeholder:
            self.machine_info_widget.set_text("\N{BULLET} Juju Default "
                                              "Placement")
            self.hardware_widget.set_text("")
        else:
            info_markup = [
                "\N{TAPE DRIVE} {}".format(self.machine.hostname),
                ('label', " ({})".format(self.machine.status))
            ]
            self.machine_info_widget.set_text(info_markup)
            self.hardware_widget.set_text(["  "] + self.hardware_info_markup())

        ad = self.controller.assignments_for_machine(self.machine)
        astr = [('label', "  Services: ")]

        for atype, al in ad.items():
            n = len(al)
            if n == 1:
                pl_s = ""
            else:
                pl_s = "s"
            if atype == AssignmentType.BareMetal:
                astr.append(('label', "\n    {} service{}"
                             " on Bare Metal: ".format(n, pl_s)))
            else:
                astr.append(('label', "\n    {} "
                             "{}{}: ".format(n, atype.name, pl_s)))
            if n == 0:
                astr.append("\N{EMPTY SET}")
            else:
                astr.append(", ".join(
                    ["\N{GEAR} {}".format(c.display_name) for c in al]))

        if self.machine == self.controller.sub_placeholder:
            assignments_text = ''
            for _, al in ad.items():
                charm_txts = ["\N{GEAR} {}".format(c.display_name) for c in al]
                assignments_text += ", ".join(charm_txts)
        else:
            assignments_text = astr

        self.assignments_widget.set_text(assignments_text)
        self.update_buttons()

    def update_buttons(self):
        buttons = []
        for at in self.actions:
            if len(at) == 2:

                def predicate(x):
                    return True

                label, func = at
            else:
                predicate, label, func = at

            if not predicate(self.machine):
                b = AttrMap(SelectableIcon(" (" + label + ")"),
                            'disabled_button', 'disabled_button_focus')
            else:
                b = AttrMap(
                    Button(label, on_press=func, user_data=self.machine),
                    'button_secondary', 'button_secondary focus')
            buttons.append((b, self.button_grid.options()))

        self.button_grid.contents = buttons
Ejemplo n.º 3
0
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(('subheading', "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)
Ejemplo n.º 4
0
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 ServiceWidget(WidgetWrap):

    """A widget displaying a service and associated actions.

    charm_class - the class describing the service to display

    controller - a PlacementController instance

    actions - a list of ('label', function) pairs that will be used to
    create buttons for each machine.  The machine will be passed to
    the function as userdata.

    optionally, actions can be a 3-tuple (pred, 'label', function),
    where pred determines whether to add the button. Pred will be
    passed the charm class.

    show_constraints - display the charm's constraints

    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, charm_class, controller, actions=None,
                 show_constraints=False, show_placements=False):
        self.charm_class = charm_class
        self.controller = controller
        if actions is None:
            self.actions = []
        else:
            self.actions = actions
        self.show_constraints = show_constraints
        self.show_placements = show_placements
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        return True

    def build_widgets(self):
        dn = self.charm_class.display_name
        self.title_markup = ["\N{GEAR} {}".format(dn), ""]
        if self.charm_class.summary != "":
            self.title_markup.append("\n {}\n".format(self.charm_class.summary))

        self.charm_info_widget = Text(self.title_markup)
        self.placements_widget = Text("")

        if self.charm_class.subordinate:
            c_str = [('label', "  (subordinate charm)")]
        elif len(self.charm_class.constraints) == 0:
            c_str = [('label', "  no constraints set")]
        else:
            cpairs = [format_constraint(k, v) for k, v in
                      self.charm_class.constraints.items()]
            c_str = [('label', "  constraints: "), ', '.join(cpairs)]
        self.constraints_widget = Text(c_str)

        self.buttons = []

        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'right')

        pl = [self.charm_info_widget]

        if self.show_placements:
            pl.append(self.placements_widget)
        if self.show_constraints:
            pl.append(self.constraints_widget)
        pl.append(self.button_grid)

        p = Pile(pl)
        return Padding(p, left=2, right=2)

    def update(self):
        mstr = [""]

        state, cons, deps = self.controller.get_charm_state(self.charm_class)

        if state == CharmState.REQUIRED:
            p = self.controller.get_assignments(self.charm_class)
            d = self.controller.get_deployments(self.charm_class)
            nr = self.charm_class.required_num_units()
            info_str = " ({} of {} placed".format(len(p), nr)
            if len(d) > 0:
                info_str += ", {} deployed)".format(len(d))
            else:
                info_str += ")"

            # Add hint to explain why a dep showed up in required
            if len(p) == 0 and len(deps) > 0:
                dep_str = ", ".join([c.display_name for c in deps])
                info_str += " - required by {}".format(dep_str)

            self.title_markup[1] = ('info', info_str)
            self.charm_info_widget.set_text(self.title_markup)

        elif state == CharmState.CONFLICTED:
            con_str = ", ".join([c.display_name for c in cons])
            self.title_markup[1] = ('error_icon',
                                    ' - Conflicts with {}'.format(con_str))
            self.charm_info_widget.set_text(self.title_markup)
        elif state == CharmState.OPTIONAL:
            self.title_markup[1] = ""
            self.charm_info_widget.set_text(self.title_markup)

        def string_for_placement_dict(d):
            s = []
            for atype, ml in d.items():
                n = len(ml)
                s.append(('label', "    {} ({}): ".format(atype.name, n)))
                if len(ml) == 0:
                    s.append("\N{DOTTED CIRCLE}")
                else:
                    s.append(", ".join(["\N{TAPE DRIVE} {}".format(m.hostname)
                                        for m in ml]))
            if len(s) == 0:
                return [('label', "None")]
            return s
        mstr += ["    ", ('label', "Assignments: ")]
        ad = self.controller.get_assignments(self.charm_class)
        dd = self.controller.get_deployments(self.charm_class)
        mstr += string_for_placement_dict(ad)
        mstr += ["\n    ", ('label', "Deployments: ")]
        mstr += string_for_placement_dict(dd)

        self.placements_widget.set_text(mstr)

        self.update_buttons()

    def update_buttons(self):
        buttons = []
        for at in self.actions:
            if len(at) == 2:
                def predicate(x):
                    return True
                label, func = at
            else:
                predicate, label, func = at

            if not predicate(self.charm_class):
                b = AttrMap(SelectableIcon(" (" + label + ")"),
                            'disabled_button', 'disabled_button_focus')
            else:
                b = AttrMap(Button(label, on_press=func,
                                   user_data=self.charm_class),
                            'button_secondary', 'button_secondary focus')
            buttons.append((b, self.button_grid.options()))

        self.button_grid.contents = buttons
class MachineWidget(WidgetWrap):

    """A widget displaying a service and associated actions.

    machine - the machine to display

    controller - a PlacementController instance

    actions - a list of ('label', function) pairs that wil be used to
    create buttons for each machine.  The machine will be passed to
    the function as userdata.

    optionally, actions can be a 3-tuple (pred, 'label', function),
    where pred determines whether to add the button. Pred will be
    passed the charm class.

    show_hardware - display hardware details about this machine

    show_assignments - display info about which charms are assigned
    and what assignment type (LXC, KVM, etc) they have.
    """

    def __init__(self, machine, controller, actions=None,
                 show_hardware=False, show_assignments=True):
        self.machine = machine
        self.controller = controller
        if actions is None:
            self.actions = []
        else:
            self.actions = actions
        self.show_hardware = show_hardware
        self.show_assignments = show_assignments
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        return True

    def hardware_info_markup(self):
        m = self.machine
        return [('label', 'arch'), ' {}  '.format(m.arch),
                ('label', 'cores'), ' {}  '.format(m.cpu_cores),
                ('label', 'mem'), ' {}  '.format(m.mem),
                ('label', 'storage'), ' {}'.format(m.storage)]

    def build_widgets(self):

        self.machine_info_widget = Text("")
        self.assignments_widget = Text("")
        self.hardware_widget = Text("")

        self.buttons = []
        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'right')

        pl = [Divider(' '), self.machine_info_widget]
        if self.show_hardware:
            pl.append(self.hardware_widget)
        if self.show_assignments:
            pl += [Divider(' '), self.assignments_widget]
        pl.append(self.button_grid)

        p = Pile(pl)

        return Padding(p, left=2, right=2)

    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()
        if self.machine == self.controller.sub_placeholder:
            self.machine_info_widget.set_text("\N{BULLET} Subordinate Charms")
            self.hardware_widget.set_text("")
        elif self.machine == self.controller.def_placeholder:
            self.machine_info_widget.set_text("\N{BULLET} Juju Default "
                                              "Placement")
            self.hardware_widget.set_text("")
        else:
            info_markup = ["\N{TAPE DRIVE} {}".format(self.machine.hostname),
                           ('label', " ({})".format(self.machine.status))]
            self.machine_info_widget.set_text(info_markup)
            self.hardware_widget.set_text(["  "] + self.hardware_info_markup())

        ad = self.controller.assignments_for_machine(self.machine)
        astr = [('label', "  Services: ")]

        for atype, al in ad.items():
            n = len(al)
            if n == 1:
                pl_s = ""
            else:
                pl_s = "s"
            if atype == AssignmentType.BareMetal:
                astr.append(('label', "\n    {} service{}"
                             " on Bare Metal: ".format(n, pl_s)))
            else:
                astr.append(('label', "\n    {} "
                             "{}{}: ".format(n, atype.name, pl_s)))
            if n == 0:
                astr.append("\N{EMPTY SET}")
            else:
                astr.append(", ".join(["\N{GEAR} {}".format(c.display_name)
                                       for c in al]))

        if self.machine == self.controller.sub_placeholder:
            assignments_text = ''
            for _, al in ad.items():
                charm_txts = ["\N{GEAR} {}".format(c.display_name)
                              for c in al]
                assignments_text += ", ".join(charm_txts)
        else:
            assignments_text = astr

        self.assignments_widget.set_text(assignments_text)
        self.update_buttons()

    def update_buttons(self):
        buttons = []
        for at in self.actions:
            if len(at) == 2:
                def predicate(x):
                    return True
                label, func = at
            else:
                predicate, label, func = at

            if not predicate(self.machine):
                b = AttrMap(SelectableIcon(" (" + label + ")"),
                            'disabled_button', 'disabled_button_focus')
            else:
                b = AttrMap(Button(label, on_press=func,
                                   user_data=self.machine),
                            'button_secondary', 'button_secondary focus')
            buttons.append((b, self.button_grid.options()))

        self.button_grid.contents = buttons
class ServiceWidget(WidgetWrap):
    """A widget displaying a service and associated actions.

    charm_class - the class describing the service to display

    controller - a PlacementController instance

    actions - a list of ('label', function) pairs that will be used to
    create buttons for each machine.  The machine will be passed to
    the function as userdata.

    optionally, actions can be a 3-tuple (pred, 'label', function),
    where pred determines whether to add the button. Pred will be
    passed the charm class.

    show_constraints - display the charm's constraints

    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,
                 charm_class,
                 controller,
                 actions=None,
                 show_constraints=False,
                 show_placements=False):
        self.charm_class = charm_class
        self.controller = controller
        if actions is None:
            self.actions = []
        else:
            self.actions = actions
        self.show_constraints = show_constraints
        self.show_placements = show_placements
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        return True

    def build_widgets(self):
        dn = self.charm_class.display_name
        self.title_markup = ["\N{GEAR} {}".format(dn), ""]

        self.charm_info_widget = Text(self.title_markup)
        self.placements_widget = Text("")

        if self.charm_class.subordinate:
            c_str = [('label', "  (subordinate charm)")]
        elif len(self.charm_class.constraints) == 0:
            c_str = [('label', "  no constraints set")]
        else:
            cpairs = [
                format_constraint(k, v)
                for k, v in self.charm_class.constraints.items()
            ]
            c_str = [('label', "  constraints: "), ', '.join(cpairs)]
        self.constraints_widget = Text(c_str)

        self.buttons = []

        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'right')

        pl = [self.charm_info_widget]

        if self.show_placements:
            pl.append(self.placements_widget)
        if self.show_constraints:
            pl.append(self.constraints_widget)
        pl.append(self.button_grid)

        p = Pile(pl)
        return Padding(p, left=2, right=2)

    def update(self):
        mstr = [""]

        state, cons, deps = self.controller.get_charm_state(self.charm_class)

        if state == CharmState.REQUIRED:
            p = self.controller.get_assignments(self.charm_class)
            d = self.controller.get_deployments(self.charm_class)
            nr = self.charm_class.required_num_units()
            info_str = " ({} of {} placed".format(len(p), nr)
            if len(d) > 0:
                info_str += ", {} deployed)".format(len(d))
            else:
                info_str += ")"

            # Add hint to explain why a dep showed up in required
            if len(p) == 0 and len(deps) > 0:
                dep_str = ", ".join([c.display_name for c in deps])
                info_str += " - required by {}".format(dep_str)

            self.title_markup[1] = ('info', info_str)
            self.charm_info_widget.set_text(self.title_markup)

        elif state == CharmState.CONFLICTED:
            con_str = ", ".join([c.display_name for c in cons])
            self.title_markup[1] = ('error_icon',
                                    ' - Conflicts with {}'.format(con_str))
            self.charm_info_widget.set_text(self.title_markup)
        elif state == CharmState.OPTIONAL:
            self.title_markup[1] = ""
            self.charm_info_widget.set_text(self.title_markup)

        def string_for_placement_dict(d):
            s = []
            for atype, ml in d.items():
                n = len(ml)
                s.append(('label', "    {} ({}): ".format(atype.name, n)))
                if len(ml) == 0:
                    s.append("\N{DOTTED CIRCLE}")
                else:
                    s.append(", ".join(
                        ["\N{TAPE DRIVE} {}".format(m.hostname) for m in ml]))
            if len(s) == 0:
                return [('label', "None")]
            return s

        mstr += ["    ", ('label', "Assignments: ")]
        ad = self.controller.get_assignments(self.charm_class)
        dd = self.controller.get_deployments(self.charm_class)
        mstr += string_for_placement_dict(ad)
        mstr += ["\n    ", ('label', "Deployments: ")]
        mstr += string_for_placement_dict(dd)

        self.placements_widget.set_text(mstr)

        self.update_buttons()

    def update_buttons(self):
        buttons = []
        for at in self.actions:
            if len(at) == 2:

                def predicate(x):
                    return True

                label, func = at
            else:
                predicate, label, func = at

            if not predicate(self.charm_class):
                b = AttrMap(SelectableIcon(" (" + label + ")"),
                            'disabled_button', 'disabled_button_focus')
            else:
                b = AttrMap(
                    Button(label, on_press=func, user_data=self.charm_class),
                    'button_secondary', 'button_secondary focus')
            buttons.append((b, self.button_grid.options()))

        self.button_grid.contents = buttons
Ejemplo n.º 9
0
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)

        actions = [("Choose Machine",
                    self.placement_view.do_show_machine_chooser)]
        self.required_services_list = ServicesList(self.placement_controller,
                                                   actions,
                                                   unplaced_only=True,
                                                   show_type='required',
                                                   show_constraints=True,
                                                   title="Required Services")
        self.additional_services_list = ServicesList(self.placement_controller,
                                                     actions,
                                                     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(('subheading', "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 = []

        if len(self.placement_controller.unplaced_services) == 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())
Ejemplo n.º 10
0
class ServiceWidget(WidgetWrap):

    """A widget displaying a service and associated actions.

    charm_class - the class describing the service to display

    controller - a PlacementController instance

    actions - a list of ('label', function) pairs that will be used to
    create buttons for each machine.  The machine will be passed to
    the function as userdata.

    optionally, actions can be a 3-tuple (pred, 'label', function),
    where pred determines whether to add the button. Pred will be
    passed the charm class.

    show_constraints - display the charm's constraints

    show_assignments - display the machine(s) currently assigned to
    host this service

    """

    def __init__(self, charm_class, controller, actions=None,
                 show_constraints=False, show_assignments=False):
        self.charm_class = charm_class
        self.controller = controller
        if actions is None:
            self.actions = []
        else:
            self.actions = actions
        self.show_constraints = show_constraints
        self.show_assignments = show_assignments
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        return True

    def build_widgets(self):
        dn = self.charm_class.display_name
        self.title_markup = ["\N{GEAR} {}".format(dn), ""]

        self.charm_info_widget = Text(self.title_markup)
        self.assignments_widget = Text("")

        if len(self.charm_class.constraints) == 0:
            c_str = [('label', "  no constraints set")]
        else:
            cpairs = [format_constraint(k, v) for k, v in
                      self.charm_class.constraints.items()]
            c_str = [('label', "  constraints: "), ', '.join(cpairs)]
        self.constraints_widget = Text(c_str)

        self.buttons = []

        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'right')

        pl = [self.charm_info_widget]
        if self.show_assignments:
            pl.append(self.assignments_widget)
        if self.show_constraints:
            pl.append(self.constraints_widget)
        pl.append(self.button_grid)

        p = Pile(pl)
        return Padding(p, left=2, right=2)

    def update(self):
        md = self.controller.machines_for_charm(self.charm_class)
        mstr = [""]

        if self.controller.service_is_required(self.charm_class):
            np = self.controller.machine_count_for_charm(self.charm_class)
            nr = self.charm_class.required_num_units()
            self.title_markup[1] = ('info',
                                    " ({} of {} placed)".format(np, nr))
            self.charm_info_widget.set_text(self.title_markup)

        for atype, ml in md.items():
            n = len(ml)
            mstr.append(('label', "    {} ({}): ".format(atype.name, n)))
            if len(ml) == 0:
                mstr.append("\N{DOTTED CIRCLE}")
            else:
                mstr.append(", ".join(["\N{TAPE DRIVE} {}".format(m.hostname)
                                       for m in ml]))
            mstr.append("\n")
        self.assignments_widget.set_text(mstr)

        self.update_buttons()

    def update_buttons(self):
        buttons = []
        for at in self.actions:
            if len(at) == 2:
                predicate = lambda x: True
                label, func = at
            else:
                predicate, label, func = at

            if not predicate(self.charm_class):
                b = AttrMap(SelectableIcon(" (" + label + ")"),
                            'disabled_button', 'disabled_button_focus')
            else:
                b = AttrMap(Button(label, on_press=func,
                                   user_data=self.charm_class),
                            'button_secondary', 'button_secondary focus')
            buttons.append((b, self.button_grid.options()))

        self.button_grid.contents = buttons