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")
Exemplo n.º 2
0
    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,
                                                     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 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)
Exemplo n.º 4
0
    def build_widgets(self):
        title_text = Text([("body", self.name)],
                          align="center")

        desc_text = Text(["\n", strip_solo_dots(self.description)])

        self.reset_button = PlainButton("Reset to Default", self.do_reset)
        if self.optype == OptionType.BOOLEAN:
            self.control = CheckBox(self.name, state=bool(self.current_value))

        elif self.optype == OptionType.INT:
            self.control = IntEdit(caption="{}: ".format(self.name),
                                   default=self.current_value)
        elif self.optype == OptionType.STRING:
            edit_text = self.current_value or ""
            self.control = StringEditor(
                caption="{}: ".format(self.name),
                edit_text=edit_text)
        else:
            raise Exception("Unknown option type")

        if self.optype == OptionType.STRING:
            connect_signal(self.control._edit, 'change',
                           self.handle_value_changed)
        else:
            connect_signal(self.control, 'change',
                           self.handle_value_changed)

        button_grid = GridFlow([self.reset_button],
                               36, 1, 0, 'right')

        return Pile([Divider(), title_text, desc_text, self.control,
                     button_grid])
Exemplo n.º 5
0
    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,
                                                     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 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 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")
Exemplo n.º 8
0
    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")
Exemplo n.º 9
0
    def __init__(self):
        self.screen = Screen()
        self.screen.set_input_timeouts(max_wait=0)
        self.steps = GridFlow([], 20, 2, 1, 'left')
        self.progress = SimpleFocusListWalker([])
        self.log = SimpleFocusListWalker([])

        self.widget = AttrMap(
            LineBox(Pile([
                ('fixed', 6, AttrMap(Filler(self.steps), 'default')),
                ('fixed', 1, Filler(Divider('\u2500'))),
                ('fixed', 3, ListBox(self.progress)),
                AttrMap(LineBox(ListBox(self.log), title='Message log'),
                        'default')
            ]),
                    title='Indico 1.2 -> 2.0 migration'), 'global_frame')

        self.screen.register_palette(
            [('green', 'light green', ''), ('white', 'white', ''),
             ('red', 'dark red', ''), ('yellow', 'yellow', ''),
             ('progress_empty', 'black',
              'light gray'), ('progress_progress', 'light cyan', 'light gray'),
             ('progress_done', 'black', 'light cyan'),
             ('box', 'white', 'dark gray'), ('step_done', 'light green', ''),
             ('step_working', 'dark gray',
              ''), ('global_frame', 'light cyan',
                    ''), ('fill', 'light cyan', 'dark cyan'),
             ('done', 'white', 'dark green'), ('eta', 'yellow', 'dark gray')] +
            generate_urwid_palette(PALETTE))
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
    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
Exemplo n.º 12
0
    def _simple_header_widgets(self, title):
        b = PlainButton("Back to Charm Store", on_press=self.show_default_view)
        self.back_to_mainview_button = AttrMap(b, 'button_secondary',
                                               'button_secondary focus')
        button_grid = GridFlow([self.back_to_mainview_button], 36, 1, 0,
                               'center')

        return [
            Divider(),
            Text(('body', title), align='center'),
            Divider(), button_grid
        ]
Exemplo n.º 13
0
    def build_widgets(self):
        self.deploy_ok_msg = ("\u2713 All the required OpenStack services are "
                              "placed on a machine, and you can now deploy.")

        self.deploy_button = AttrMap(Button("Deploy", on_press=self.do_deploy),
                                     'button_primary', 'button_primary focus')
        self.deploy_grid = GridFlow([self.deploy_button], 10, 1, 0, 'center')

        self.unplaced_msg = "Some required services are still unassigned."

        self.main_pile = Pile([Divider()])
        return self.main_pile
Exemplo n.º 14
0
 def _make(self, calendar):
     # if we do this as single gridflow then focus doesn't move down into dates
     # so instead put names in pile
     names = GridFlow(list(map(lambda d: Text(('plain', d)), DAYS2)), 2, 1,
                      0, 'left')
     prev = dt.date(
         self._date.year if self._date.month > 1 else self._date.year - 1,
         self._date.month - 1 if self._date.month > 1 else 12, 1)
     next = dt.date(
         self._date.year if self._date.month < 12 else self._date.year + 1,
         self._date.month + 1 if self._date.month < 12 else 1, 1)
     prev_days = monthrange(prev.year, prev.month)[1]
     curr_days = monthrange(self._date.year, self._date.month)[1]
     first_day = dt.date(self._date.year, self._date.month,
                         1).weekday()  # mon 0
     total_days = first_day + curr_days
     extra_days = 7 - total_days % 7
     if extra_days == 7:
         extra_days = 0
     else:
         total_days += extra_days
     dates = [
         FocusAttr(Day(dt.date(prev.year, prev.month, i)), 'unimportant')
         for i in range(prev_days - first_day + 1, prev_days + 1)
     ]
     dates.extend([
         FocusAttr(Day(dt.date(self._date.year, self._date.month, i)),
                   plain='selected' if i == self._date.day else 'plain',
                   focus='selected-focus'
                   if i == self._date.day else 'plain-focus')
         for i in range(1, curr_days + 1)
     ])
     dates.extend([
         FocusAttr(Day(dt.date(next.year, next.month, i)), 'unimportant')
         for i in range(1, extra_days + 1)
     ])
     for day in dates:
         connect_signal(day._original_widget, 'click', calendar.date_change)
     dates = GridFlow(dates, 2, 1, 0, 'left')
     return Pile([names, dates])
Exemplo n.º 15
0
    def __init__(self,
                 height,
                 directory=".",
                 file="",
                 attr=(None, None),
                 show_hidden=False):
        """
        height -- height of the directory list and the file list
        directory, file -- default selection
        attr -- (inner selectable widgets, selected widgets)
        show_hidden -- If True, hidden files are shown by default.
        """

        self.directory = abspath(directory)
        self.file = ""
        self.attr = attr
        self.height = height
        self.show_hidden = show_hidden

        # Create dummy widgets for directory and file display:
        self.dir_widget = AttrWrap(
            BoxAdapter(ListBox([self._blank]), self.height), self.attr[0])
        self.file_widget = AttrWrap(
            BoxAdapter(ListBox([self._blank]), self.height), self.attr[0])

        columns = Columns([self.dir_widget, self.file_widget], 1)

        # Selection widget:
        self.select_widget = AttrWrap(Edit("", ""), self.attr[0], self.attr[1])

        # Buttons and checkbox:
        button_widgets = [
            AttrWrap(Button(button, self._action), attr[0], attr[1])
            for button in ["OK", "Cancel"]
        ]
        button_grid = GridFlow(button_widgets, 12, 2, 1, 'center')

        button_cols = Columns([
            CheckBox(self.SHOW_HIDDEN_TEXT, self.show_hidden, False,
                     self._toggle_hidden), button_grid
        ])

        self.outer_widget = Pile([
            columns, self._blank,
            Text(self.SELECTION_TEXT), self.select_widget, self._blank,
            button_cols
        ])

        self.update_widgets()

        WidgetWrap.__init__(self, self.outer_widget)
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    def get_services_header(self):
        b = PlainButton("Clear All Placements", on_press=self.do_clear_all)
        self.clear_all_button = AttrMap(b, 'button_secondary',
                                        'button_secondary focus')

        self.services_buttons = [self.clear_all_button]
        self.services_button_grid = GridFlow(self.services_buttons, 36, 1, 0,
                                             'center')

        ws = [Divider(), Text(("body", "Services"), align='center'), Divider()]
        if self.has_maas:
            ws.append(self.services_button_grid)

        return Pile(ws)
Exemplo n.º 18
0
    def build_widgets(self):
        desc_text = Text(["\n", strip_solo_dots(self.description)])

        self.reset_button = PlainButton("Reset to Default", self.do_reset)
        if self.optype == OptionType.BOOLEAN:
            self.control = CheckBox('', state=bool(self.current_value))
            self.wrapped_control = self.control
        elif self.optype == OptionType.INT:
            self.control = IntEdit(default=self.current_value)
            self.wrapped_control = Color.string_input(
                self.control, focus_map='string_input focus')
        elif self.optype == OptionType.STRING:
            edit_text = self.current_value or ""
            self.control = StringEditor(edit_text=edit_text)
            self.wrapped_control = Color.string_input(
                self.control, focus_map='string_input focus')
        elif self.optype == OptionType.FLOAT:
            edit_text = str(self.current_value)
            self.control = StringEditor(edit_text=edit_text)
            self.wrapped_control = Color.string_input(
                self.control, focus_map='string_input focus')
        else:
            raise Exception("Unknown option type")

        self.control_columns = Columns(
            [('pack', Text("{}:".format(self.name), align='right')),
             (80, self.wrapped_control)],
            dividechars=1)

        if self.optype in [OptionType.STRING, OptionType.FLOAT]:
            connect_signal(self.control._edit, 'change',
                           self.handle_value_changed)
        else:
            connect_signal(self.control, 'change', self.handle_value_changed)

        button_grid = GridFlow([
            Color.button_secondary(self.reset_button,
                                   focus_map='button_secondary focus')
        ], 36, 1, 0, 'right')

        return Pile([
            Padding.line_break(""),
            Padding.left(self.control_columns, left=1),
            Padding.left(desc_text, left=2), button_grid
        ])
    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 build_widgets(self):

        if self.charm_class.allow_multi_units:
            machine_string = "machines"
            plural_string = "s"
        else:
            machine_string = "a machine"
            plural_string = ""
        instructions = Text("Select {} to host {}".format(
            machine_string, self.charm_class.display_name))

        self.service_widget = ServiceWidget(self.charm_class,
                                            self.controller,
                                            show_constraints=True,
                                            show_placements=True)

        all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal',
                        self.do_select_baremetal),
                       (AssignmentType.LXC, 'Add as LXC', self.do_select_lxc),
                       (AssignmentType.KVM, 'Add as KVM', self.do_select_kvm)]

        actions = [(label, func) for atype, label, func in all_actions
                   if atype in self.charm_class.allowed_assignment_types]

        constraints = self.charm_class.constraints
        # NOTE: show_assignments=False is a WORKAROUND for #194
        self.machines_list = MachinesList(self.controller,
                                          actions,
                                          constraints=constraints,
                                          show_hardware=True,
                                          show_assignments=False)
        self.machines_list.update()
        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.service_widget,
            Divider(), self.machines_list
        ])

        return LineBox(p, title="Select Machine{}".format(plural_string))
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()
Exemplo n.º 22
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)
Exemplo n.º 23
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
Exemplo n.º 24
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_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
Exemplo n.º 25
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())
Exemplo n.º 26
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
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 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()
Exemplo n.º 29
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 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