def build_widgets(self):
        self.message = Text("Please review available machines in MAAS", align="center")

        self.button_pile = Pile([])

        self.main_pile = Pile([self.message, Divider(), self.button_pile])
        return Filler(self.main_pile, valign="middle")
class DeployView(WidgetWrap):

    def __init__(self, display_controller, placement_controller,
                 placement_view):
        self.display_controller = display_controller
        self.placement_controller = placement_controller
        self.placement_view = placement_view
        self.prev_status = None
        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def selectable(self):
        return True

    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

    def update(self):
        changed = self.prev_status != self.placement_controller.can_deploy()

        if self.placement_controller.can_deploy():
            if changed:
                self.show_deploy_button()
        else:
            self.main_pile.contents[0] = (Divider(),
                                          self.main_pile.options())
            if changed:
                self.display_controller.status_error_message(self.unplaced_msg)

        self.prev_status = self.placement_controller.can_deploy()

    def show_deploy_button(self):
        self.main_pile.contents[0] = (AttrMap(self.deploy_grid,
                                              'deploy_highlight_start'),
                                      self.main_pile.options())
        self.display_controller.status_info_message(self.deploy_ok_msg)

    def do_deploy(self, sender):
        self.placement_view.do_deploy_cb()
 def build_widgets(self):
     widgets = []
     if self.title:
         widgets = [Text(self.title), Divider(' ')]
     widgets += self.service_widgets
     self.service_pile = Pile(widgets)
     return self.service_pile
Example #4
0
class Header(WidgetWrap):

    TITLE_TEXT = "Ubuntu OpenStack Installer - Dashboard"

    def __init__(self):
        self.text = Text(self.TITLE_TEXT)
        self.widget = Color.frame_header(self.text)
        self.pile = Pile([self.widget, Text("")])
        self.set_show_add_units_hotkey(False)
        super().__init__(self.pile)

    def set_openstack_rel(self, release):
        self.text.set_text("{} ({})".format(self.TITLE_TEXT, release))

    def set_show_add_units_hotkey(self, show):
        self.show_add_units = show
        self.update()

    def update(self):
        if self.show_add_units:
            add_unit_string = "(A)dd Services \N{BULLET}"
        else:
            add_unit_string = ""
        tw = Color.frame_subheader(
            Text(add_unit_string + " (H)elp \N{BULLET} " "(R)efresh \N{BULLET} (Q)uit", align="center")
        )
        self.pile.contents[1] = (tw, self.pile.options())
Example #5
0
    def __init__(self, filename, title=None, timeout=10, interval=0.1):#{{{
        self.filename = filename
        self.interval = interval
        self.timeout = timeout

        if title is None:
            title = "IMPRIMIENDO"

        msg = Text("Esperando la respuesta de la Impresora Fiscal ...", align='left')
        self.eta = Text("", align='left')

        self.dummy = Text("")
        self.dummy._selectable = True
        self.dummy.keypress = lambda s,k : None

        self.buttons = Columns([
            AttrMap(Button("Seguir esperando", on_press=self.remove_buttons),'dialog.button', 'dialog.button.focus'),
            AttrMap(Button("Cancelar Impresion", on_press=lambda *w: self.quit()), 'dialog.button', 'dialog.button.focus'),
        ], dividechars=1)

        self.content = Pile([
            msg,
            Divider(),
            self.eta,
            self.dummy,
        ])

        self.__super.__init__(
            self.content,
            title=title,
            attr_style='dialog.waitfiscal',
            title_attr_style='dialog.waitfiscal.title',
            height=None,
            width=60,
        )
    def build_widgets(self):

        self.action_button_cols = Columns([])
        self.action_buttons = []
        self.build_unselected_widgets()
        self.pile = Pile([self.unselected_columns])
        return self.pile
    def build_widgets(self, title_widgets):
        if title_widgets is None:
            title_widgets = [Text("Machines", align='center')]

        self.filter_edit_box = FilterBox(self.handle_filter_change)

        header_widgets = title_widgets + [Divider()]

        if self.show_filter_box:
            header_widgets += [self.filter_edit_box, Divider()]
        labels = ["ID", "Cores", "Memory (GiB)", "Storage (GiB)"]
        if self.show_assignments:
            labels += ["Assignments", ""]
        else:
            labels += [""]

        header_label_col = Columns([Text(m) for m in labels],
                                   dividechars=2)
        header_widgets.append(header_label_col)
        self.header_padding = len(header_widgets)
        self.add_new_button = AttrMap(
            PlainButton("Add New Machine",
                        on_press=self.do_add_machine),
            'button_secondary',
            'button_secondary focus')
        self.add_new_cols = Columns([Text(s) for s in
                                     [' ', ' ', ' ', ' ', ' ']] +
                                    [self.add_new_button], dividechars=2)
        self.machine_pile = Pile(header_widgets + self.machine_widgets +
                                 [self.add_new_cols])
        return self.machine_pile
Example #8
0
    def __init__(self, title=None, cls=None):
        if cls is None:
            cls = type('Dummy', (object,), {'__init__': lambda *a, **k: None, 'run': lambda *a: None})
        self.cls = cls
        self._obj = None

        _edit_cancel = lambda *w: self.focus_button(1)

        self.codigo_box = MaeterCodeBox(max_length=12, align='right')
        connect_signal(self.codigo_box, 'focus-in', highlight_focus_in),
        connect_signal(self.codigo_box, 'edit-done', self.on_codigo_edit_done)
        connect_signal(self.codigo_box, 'edit-cancel', _edit_cancel)
        connect_signal(self.codigo_box, 'search-client', self.on_client_search)

        self.nombre = Text(u'')

        client_row = Columns([
            ('fixed', 8, AttrMap(Text("Cliente", align='right'), 'dialog.selectdate.label')),
            ('fixed', 6, AttrMap(self.codigo_box, 'dialog.selectdate.input', 'dialog.selectdate.input.focus')),
            AttrMap(self.nombre, 'dialog.selectdate.label'),
        ], dividechars=1)

        self.content = Pile([
            client_row,
            Divider(),
        ])

        buttons = [("Continuar", self.run_list), ("Cancelar", self.quit)]

        self.__super.__init__(self.content, buttons,
                title=title or u'<Falta titulo>',
                height=None,
                width=60,
                attr_style='dialog.selectdate',
                title_attr_style='dialog.selectdate.title')
    def build_widgets(self):

        self.button = MenuSelectButton("I AM A MACHINE", self.do_select)
        self.action_button_cols = Columns([])
        self.action_buttons = []

        self.pile = Pile([self.button])
        return self.pile
    def build_widgets(self):

        self.button = MenuSelectButton("I AM A SERVICE", self.do_select)

        self.action_button_cols = Columns([], dividechars=1)
        self.action_buttons = []

        self.pile = Pile([self.button])
        return self.pile
Example #11
0
 def __init__(self, walker, **kwargs):
     if not isinstance(walker, TreeListWalker):
         walker = TreeListWalker(walker)
     self._walker = walker
     self._lines = []
     self.loadlines()
     logging.debug('lines:\n\n%s' % str(self._lines))
     self._pile = Pile(self._lines)
     self.__super.__init__(self._pile)
    def build_widgets(self):
        self.description_w = Text("Description Loading…")
        self.readme_w = Text("README Loading…")
        self.scale_edit = IntegerEditor(default=self.service.num_units)
        connect_signal(self.scale_edit._edit, 'change',
                       self.handle_scale_changed)
        self.skip_rest_button = PlainButton(
            "Deploy all {} Remaining Applications with Bundle Defaults".format(
                self.n_remaining),
            self.do_skip_rest
        )
        col = Columns(
            [
                (6, Text('Units:', align='right')),
                (15,
                 Color.string_input(self.scale_edit,
                                    focus_map='string_input focus'))
            ], dividechars=1
        )

        if self.n_remaining == 0:
            buttons = [Padding.right_50(Color.button_primary(
                PlainButton("Deploy and Continue",
                            self.do_deploy),
                focus_map='button_primary focus'))]
        else:
            buttons = [
                Padding.right_50(Color.button_primary(
                    PlainButton(
                        "Deploy and Configure Next Application",
                        self.do_deploy),
                    focus_map='button_primary focus')),
                Padding.right_50(
                    Color.button_secondary(
                        self.skip_rest_button,
                        focus_map='button_secondary focus'))]

        ws = [Text("{} of {}: {}".format(self.idx+1, self.n_total,
                                         self.service.service_name.upper())),
              Padding.center(HR()),
              Padding.center(self.description_w, left=2),
              Padding.line_break(""),
              Padding.center(self.readme_w, left=2),
              Padding.center(HR())]

        if not self.service.subordinate:
            ws.append(Padding.left(col, left=1))

        ws.append(Padding.line_break(""))
        ws += buttons

        self.pile = Pile(ws)
        return Padding.center_90(Filler(self.pile, valign="top"))
    def build_widgets(self, title_widgets):
        if title_widgets is None:
            if len(self.constraints) > 0:
                cstr = " matching constraints"
            else:
                cstr = ""

            title_widgets = Text("Machines" + cstr, align="center")

        self.filter_edit_box = FilterBox(self.handle_filter_change)

        self.machine_pile = Pile([title_widgets, Divider(), self.filter_edit_box] + self.machine_widgets)
        return self.machine_pile
    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
    def build_widgets(self):

        def has_services_p(m):
            pc = self.placement_controller
            n = sum([len(al) for at, al in
                     pc.assignments_for_machine(m).items()])
            return n > 0

        clear_machine_func = self.placement_view.do_clear_machine
        show_chooser_func = self.placement_view.do_show_service_chooser

        self.open_maas_button = AttrMap(Button("Open in Browser",
                                               on_press=self.browse_maas),
                                        'button_secondary',
                                        'button_secondary focus')

        bc = self.placement_view.config.juju_env['bootstrap-config']
        maasname = "'{}' <{}>".format(bc['name'], bc['maas-server'])
        maastitle = "Connected to MAAS {}".format(maasname)
        tw = Columns([Text(maastitle),
                      Padding(self.open_maas_button, align='right',
                              width=BUTTON_SIZE, right=2)])

        self.machines_list = MachinesList(self.placement_controller,
                                          [(has_services_p,
                                            'Clear All Services',
                                            clear_machine_func),
                                           (has_services_p,
                                            'Remove Some Services',
                                            show_chooser_func)],
                                          show_hardware=True,
                                          title_widgets=tw)
        self.machines_list.update()

        self.machines_list_pile = Pile([self.machines_list,
                                        Divider()])

        # placeholders replaced in update() with absolute indexes, so
        # if you change this list, check update().
        pl = [Text(('body', "Machines {}".format(MetaScroll().get_text()[0])),
                   align='center'),
              Divider(),
              Pile([]),         # machines_list
              Divider()]

        self.main_pile = Pile(pl)

        return self.main_pile
    def build_widgets(self):
        self.machines_list = MachinesList(self.placement_controller,
                                          self.display_controller,
                                          None,
                                          show_hardware=True,
                                          show_assignments=False,
                                          show_placeholders=False,
                                          show_only_ready=True,
                                          title_widgets=[])

        self.machines_list.update()

        self.machines_list_pile = Pile([self.machines_list,
                                        Divider()])

        return self.machines_list_pile
Example #17
0
    def build_widgets(self, title_widgets):
        if title_widgets is None:
            if len(self.constraints) > 0:
                cstr = " matching constraints"
            else:
                cstr = ""

            title_widgets = [Text("Machines" + cstr, align='center')]

        self.filter_edit_box = FilterBox(self.handle_filter_change)

        header_widgets = title_widgets + [Divider()]

        if self.show_filter_box:
            header_widgets.append(self.filter_edit_box)

        self.header_padding = len(header_widgets)
        self.machine_pile = Pile(header_widgets + self.machine_widgets)
        return self.machine_pile
 def build_widgets(self):
     ws = [Text("Configure {}".format(
         self.application.service_name))]
     num_unit_ow = OptionWidget("Units", "int",
                                "How many units to deploy.",
                                self.application.orig_num_units,
                                current_value=self.application.num_units,
                                value_changed_callback=self.handle_scale)
     ws.append(num_unit_ow)
     ws += self.get_whitelisted_option_widgets()
     self.toggle_show_all_button_index = len(ws) + 1
     self.toggle_show_all_button = PlainButton(
         "Show Advanced Configuration",
         self.do_toggle_show_all_config)
     ws += [HR(),
            Columns([('weight', 1, Text(" ")),
                     (36, Color.button_secondary(
                         self.toggle_show_all_button))])]
     self.pile = Pile(ws)
     return Padding.center_90(Filler(self.pile, valign="top"))
Example #19
0
    def __init__(self, **kwargs):#{{{

        self.search_box = SearchBox()
        self.items_count = Text("", align='right')

        self.search_items = SimpleListWalker(self._null_list_item())
        connect_signal(self.search_items, "modified", self._update_items_count)
        self.search_list = ListBox(self.search_items)

        connect_signal(self.search_box, "edit-done", self.on_edit_done)
        connect_signal(self.search_box, "edit-cancel", lambda w: self.quit())
        connect_signal(self.search_box, "change", lambda sb, term: self.set_search_term(term))

        self._constr = self.get_item_constructor()

        self.multiple_selection = kwargs.pop('multiple_selection', False)
        self._selected_items = OrderedSet([])

        opts = {
            'height': kwargs.get('height', None),
            'width': kwargs.get('width', ('relative', 90)),
            'title': kwargs.get('title', self.title),
            'subtitle': kwargs.get('subtitle', self.subtitle),
            'compact_header': kwargs.get('compact_header', True),
        }
        kwargs.update(opts)

        self.pile = Pile([
            ('fixed', 15, AttrMap(self.search_list, 'dialog.search.item')),
            Columns([
                AttrMap(self.search_box, 'dialog.search.input'),
                ('fixed', 1, AttrMap(Divider(), 'dialog.search.input')),
                ('fixed', 4, AttrMap(self.items_count, 'dialog.search.input')),
                ]),
            ], focus_item=0)

        self.__super.__init__(self.pile, **kwargs)

        self.attr_style = "dialog.search"
        self.title_attr_style = "dialog.search.title"
        self.subtitle_attr_style = "dialog.search.subtitle"
Example #20
0
class Header(WidgetWrap):

    def __init__(self):
        self.title_widget = Color.frame_header(padding(Text(TITLE_TEXT)))
        self.pile = Pile([self.title_widget, Text("")])
        self.set_show_add_units_hotkey(False)
        super().__init__(self.pile)

    def set_show_add_units_hotkey(self, show):
        self.show_add_units = show
        self.update()

    def update(self):
        if self.show_add_units:
            add_unit_string = '(A)dd Services \N{BULLET}'
        else:
            add_unit_string = ''
        tw = Color.frame_subheader(Text(add_unit_string + ' (H)elp \N{BULLET} '
                                        '(R)efresh \N{BULLET} (Q)uit',
                                        align='center'))
        self.pile.contents[1] = (tw, self.pile.options())
Example #21
0
    def build_widgets(self, title_widgets):
        if title_widgets is None:
            if len(self.constraints) > 0:
                cstr = " matching constraints '{}'".format(
                    constraints_from_dict(self.constraints))
            else:
                cstr = ""

            title_widgets = [Text("Machines" + cstr, align='center')]

        self.filter_edit_box = FilterBox(self.handle_filter_change)

        header_widgets = title_widgets + [Divider()]

        if self.show_filter_box:
            header_widgets += [self.filter_edit_box, Divider()]
        labels = ["FQDN", "Cores", "Memory (GiB)", "Storage (GiB)", ""]
        header_label_col = Columns([Text(m) for m in labels])
        header_widgets.append(header_label_col)
        self.header_padding = len(header_widgets)
        self.machine_pile = Pile(header_widgets + self.machine_widgets)
        return self.machine_pile
Example #22
0
    def _create_login_widget(self):
        self.username_entry = Edit(align='right')
        self.username_entry.keypress = self._username_keypress
        self.password_entry = Password(align='right')
        self.password_entry.keypress = self._password_keypress

        username_row = Columns([
            ('fixed', 10, Text("Usuario:", align='right')),
            ('fixed', 10, self.username_entry),
        ])

        password_row = Columns([
            ('fixed', 10, Text("Clave:", align='right')),
            ('fixed', 10, self.password_entry),
        ])

        self.pile = Pile([
            username_row,
            Divider(),
            password_row,
        ], focus_item=0)

        self.login_widget = Filler(Columns([Divider(), self.pile, Divider()]))
Example #23
0
 def _build_widget(self):
     total_items = [Text(
         "Enter your {} credentials:".format(self.cloud.upper()))]
     total_items += [HR()]
     for k in self.input_items.keys():
         display = k
         if k.startswith('_'):
             # Don't treat 'private' keys as input
             continue
         if k.startswith('@'):
             # Strip public, not storable attribute
             display = k[1:]
         col = Columns(
             [
                 ('weight', 0.5, Text(display, align='right')),
                 Color.string_input(self.input_items[k],
                                    focus_map='string_input focus')
             ], dividechars=1
         )
         total_items.append(col)
         total_items.append(Padding.line_break(""))
     total_items.append(Text(""))
     self.pile = Pile(total_items)
     return Padding.center_60(Filler(self.pile, valign="top"))
Example #24
0
 def build_widgets(self):
     self.title = Text('')
     self.option_widgets = []
     self.pile = Pile([Divider(), self.title] + self.option_widgets)
     return self.pile
Example #25
0
 def _add_item(self, item):
     if not self.pile:
         self.pile = Pile([item])
     else:
         self.pile.contents.append((item, self.pile.options()))
Example #26
0
class MachinesColumn(WidgetWrap):
    """Shows machines or a link to MAAS to add more"""
    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):
        def has_services_p(m):
            pc = self.placement_controller
            n = sum(
                [len(al) for at, al in pc.assignments_for_machine(m).items()])
            return n > 0

        clear_machine_func = self.placement_view.do_clear_machine
        show_chooser_func = self.placement_view.do_show_service_chooser

        self.open_maas_button = AttrMap(
            Button("Open in Browser", on_press=self.browse_maas),
            'button_secondary', 'button_secondary focus')

        bc = self.placement_view.config.juju_env['bootstrap-config']
        maasname = "'{}' <{}>".format(bc['name'], bc['maas-server'])
        maastitle = "Connected to MAAS {}".format(maasname)
        tw = Columns([
            Text(maastitle),
            Padding(self.open_maas_button,
                    align='right',
                    width=BUTTON_SIZE,
                    right=2)
        ])

        self.machines_list = MachinesList(
            self.placement_controller,
            [(has_services_p, 'Clear All Services', clear_machine_func),
             (has_services_p, 'Remove Some Services', show_chooser_func)],
            show_hardware=True,
            title_widgets=tw)
        self.machines_list.update()

        self.machines_list_pile = Pile([self.machines_list, Divider()])

        # placeholders replaced in update() with absolute indexes, so
        # if you change this list, check update().
        pl = [
            Text(('subheading', "Machines"), align='center'),
            Divider(),
            Pile([]),  # machines_list
            Divider()
        ]

        self.main_pile = Pile(pl)

        return self.main_pile

    def update(self):
        self.machines_list.update()

        bc = self.placement_view.config.juju_env['bootstrap-config']
        empty_maas_msg = ("There are no available machines.\n"
                          "Open {} to add machines to "
                          "'{}':".format(bc['maas-server'], bc['name']))

        self.empty_maas_widgets = Pile([
            Text([('error_icon', "\N{WARNING SIGN} "), empty_maas_msg],
                 align='center'),
            Padding(self.open_maas_button, align='center', width=BUTTON_SIZE)
        ])

        # 2 machines is the subordinate placeholder + juju default:
        if len(self.placement_controller.machines()) == 2:
            self.main_pile.contents[2] = (self.empty_maas_widgets,
                                          self.main_pile.options())
        else:
            self.main_pile.contents[2] = (self.machines_list_pile,
                                          self.main_pile.options())

    def browse_maas(self, sender):

        bc = self.placement_view.config.juju_env['bootstrap-config']
        try:
            p = Popen(["sensible-browser", bc['maas-server']],
                      stdout=PIPE,
                      stderr=PIPE)
            outs, errs = p.communicate(timeout=5)

        except TimeoutExpired:
            # went five seconds without an error, so we assume it's
            # OK. Don't kill it, just let it go:
            return
        e = errs.decode('utf-8')
        msg = "Error opening '{}' in a browser:\n{}".format(bc['name'], e)

        w = InfoDialogWidget(msg, self.placement_view.remove_overlay)
        self.placement_view.show_overlay(w)
class MachinesColumn(WidgetWrap):

    """Shows machines or a link to MAAS to add more"""

    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):

        def has_services_p(m):
            pc = self.placement_controller
            n = sum([len(al) for at, al in
                     pc.assignments_for_machine(m).items()])
            return n > 0

        clear_machine_func = self.placement_view.do_clear_machine
        show_chooser_func = self.placement_view.do_show_service_chooser

        self.open_maas_button = AttrMap(Button("Open in Browser",
                                               on_press=self.browse_maas),
                                        'button_secondary',
                                        'button_secondary focus')

        bc = self.placement_view.config.juju_env['bootstrap-config']
        maasname = "'{}' <{}>".format(bc['name'], bc['maas-server'])
        maastitle = "Connected to MAAS {}".format(maasname)
        tw = Columns([Text(maastitle),
                      Padding(self.open_maas_button, align='right',
                              width=BUTTON_SIZE, right=2)])

        self.machines_list = MachinesList(self.placement_controller,
                                          [(has_services_p,
                                            'Clear All Services',
                                            clear_machine_func),
                                           (has_services_p,
                                            'Remove Some Services',
                                            show_chooser_func)],
                                          show_hardware=True,
                                          title_widgets=tw)
        self.machines_list.update()

        self.machines_list_pile = Pile([self.machines_list,
                                        Divider()])

        # placeholders replaced in update() with absolute indexes, so
        # if you change this list, check update().
        pl = [Text(('body', "Machines {}".format(MetaScroll().get_text()[0])),
                   align='center'),
              Divider(),
              Pile([]),         # machines_list
              Divider()]

        self.main_pile = Pile(pl)

        return self.main_pile

    def update(self):
        self.machines_list.update()

        bc = self.placement_view.config.juju_env['bootstrap-config']
        empty_maas_msg = ("There are no available machines.\n"
                          "Open {} to add machines to "
                          "'{}':".format(bc['maas-server'], bc['name']))

        self.empty_maas_widgets = Pile([Text([('error_icon',
                                               "\N{WARNING SIGN} "),
                                              empty_maas_msg],
                                             align='center'),
                                        Padding(self.open_maas_button,
                                                align='center',
                                                width=BUTTON_SIZE)])

        # 1 machine is the subordinate placeholder:
        if len(self.placement_controller.machines()) == 1:
            self.main_pile.contents[2] = (self.empty_maas_widgets,
                                          self.main_pile.options())
        else:
            self.main_pile.contents[2] = (self.machines_list_pile,
                                          self.main_pile.options())

    def browse_maas(self, sender):

        bc = self.placement_view.config.juju_env['bootstrap-config']
        try:
            p = Popen(["sensible-browser", bc['maas-server']],
                      stdout=PIPE, stderr=PIPE)
            outs, errs = p.communicate(timeout=5)

        except TimeoutExpired:
            # went five seconds without an error, so we assume it's
            # OK. Don't kill it, just let it go:
            return
        e = errs.decode('utf-8')
        msg = "Error opening '{}' in a browser:\n{}".format(bc['name'], e)

        w = InfoDialogWidget(msg, self.placement_view.remove_overlay)
        self.placement_view.show_overlay(w)
Example #28
0
def exploreFieldSet(field_set, args, options={}):
    charset = getTerminalCharset()

    ui = urwid.curses_display.Screen()
    ui.register_palette((
        ('focus', 'white', 'dark blue'),
        ('sep', 'white', 'dark red'),
        ('input', 'black', 'light gray'),
    ))

    msgs = [[], [], 0]
    hachoir_log.use_print = False

    def logger(level, prefix, text, ctxt):
        if ctxt is not None:
            c = []
            if hasattr(ctxt, "_logger"):
                c[:0] = [ctxt._logger()]
            if issubclass(ctxt.__class__, Field):
                ctxt = ctxt["/"]
            name = logger.objects.get(ctxt)
            if name:
                c[:0] = [name]
            if c:
                text = "[%s] %s" % ('|'.join(c), text)
        if not isinstance(text, str):
            text = str(text, charset)
        msgs[0].append((level, prefix, text))

    logger.objects = WeakKeyDictionary()
    hachoir_log.on_new_message = logger

    preload_fields = 1 + max(0, args.preload)

    log_count = [0, 0, 0]
    sep = Separator("log: %%u/%%u/%%u  |  %s  " % "F1: help")
    sep.set_info(*tuple(log_count))
    body = Tabbed(sep)
    help = ('help', ListBox([Text(getHelpMessage())]))
    logger.objects[field_set] = logger.objects[
        field_set.stream] = name = 'root'
    body.append((name,
                 TreeBox(charset, Node(field_set, None), preload_fields,
                         args.path, options)))

    log = BoxAdapter(ListBox(msgs[1]), 0)
    log.selectable = lambda: False
    wrapped_sep = AttrWrap(sep, 'sep')
    footer = Pile([('flow', wrapped_sep), log])

    # awful way to allow the user to hide the log widget
    log.render = lambda size, focus=False: BoxAdapter.render(
        log, size[:1], focus)
    footer.render = lambda arg, focus=False: Pile.render(
        footer, (arg[0], sep.rows(arg) + log.height), focus)

    top = Frame(body, None, footer)

    def input_enter(w):
        footer.widget_list[0] = w
        footer.set_focus(0)
        top.set_focus('footer')

    def input_leave():
        footer.widget_list[0] = wrapped_sep
        footer.set_focus(0)
        top.set_focus('body')

    input = Input(input_enter, input_leave)

    def run():
        msg = _resize = retry = 0
        events = ("window resize", )
        profile_display = args.profile_display
        while True:
            for e in events:
                try:
                    if e == "window resize":
                        size = ui.get_cols_rows()
                        resize = log.height
                    else:
                        e = top.keypress(size, e)
                        if e is None:
                            pass
                        elif e in ('f1', '?'):
                            try:
                                body.select(body.tabs.index(help))
                            except ValueError:
                                body.append(help)
                                resize = log.height
                        elif e in ('esc', 'ctrl w'):
                            body.close()
                            if body.original_widget is None:
                                return
                            resize = log.height
                        elif e == '+':
                            if log.height:
                                resize = log.height - 1
                        elif e == '-':
                            resize = log.height + 1
                        elif e == 'q':
                            return
                # except AssertionError:
                #     hachoir_log.error(getBacktrace())
                except NewTab_Stream as e:
                    stream = e.field.getSubIStream()
                    logger.objects[stream] = e = "%u/%s" % (
                        body.active, e.field.absolute_address)
                    parser = guessParser(stream)
                    if not parser:
                        hachoir_log.error("No parser found for %s" %
                                          stream.source)
                    else:
                        logger.objects[parser] = e
                        body.append((e,
                                     TreeBox(charset, Node(parser, None),
                                             preload_fields, None, options)))
                        resize = log.height
                except NeedInput as e:
                    input.do(*e.args)
                if profile_display:
                    events = events[1:]
                    break
            while True:
                if msgs[0]:
                    for level, prefix, text in msgs[0]:
                        log_count[level] += 1
                        txt = Text("[%u]%s %s" % (msg, prefix, text))
                        msg += 1
                        msgs[1].append(txt)
                        _resize += txt.rows(size[:1])
                    if log.height < _resize and (resize is None
                                                 or resize < _resize):
                        resize = _resize
                    log.set_focus(len(msgs[1]) - 1)
                    sep.set_info(*tuple(log_count))
                    msgs[0] = []
                if resize is not None:
                    body.height = size[1] - sep.rows(size[:1]) - resize
                    if body.height <= 0:
                        resize += body.height - 1
                        body.height = 1
                    log.height = resize
                    resize = None
                canvas = top.render(size, focus=True)
                if not msgs[0]:
                    _resize = retry = 0
                    break
                assert not retry
                retry += 1
            ui.draw_screen(size, canvas)
            msgs[2] = len(msgs[1])
            if profile_display and events:
                continue
            while True:
                events = ui.get_input()
                if events:
                    break

    try:
        ui.run_wrapper(run)
    except Exception:
        pending = [msg.get_text()[0] for msg in msgs[1][msgs[2]:]] + \
                  ["[*]%s %s" % (prefix, text)
                   for level, prefix, text in msgs[0]]
        if pending:
            print("\nPending messages:\n" + '\n'.join(pending))
        raise
Example #29
0
class OptionsColumn(WidgetWrap):

    """UI to edit options of a service
    """

    def __init__(self, display_controller, placement_controller,
                 placement_view, metadata_controller):
        self.placement_controller = placement_controller
        self.metadata_controller = metadata_controller
        self.service = None
        self.filter_string = ""
        self.placement_view = placement_view

        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def build_widgets(self):
        self.title = Text('')
        self.option_widgets = []
        self.pile = Pile([Divider(), self.title] + self.option_widgets)
        return self.pile

    def refresh(self):
        self.set_service(self.service)

    def set_service(self, service):
        self.service = service
        self.metadata_controller.add_charm(service.csid.as_str_without_rev())
        self.pile.contents = self.pile.contents[:2]
        self.option_widgets = []

    def update(self):
        if self.service is None:
            return

        self.title.set_text(('body', "Edit Options for {}".format(
            self.service.service_name)))

        if len(self.option_widgets) == 0:
            if self.filter_string != "":
                self.title.set_text(
                    ('body',
                     "No options match '{}'".format(self.filter_string)))
            else:
                self.title.set_text(('body', "Loading Options..."))
        else:
            self.title.set_text(('body', "Edit Options: (Changes are "
                                 "saved immediately)"))

        mc = self.metadata_controller
        options = mc.get_options(self.service.csid.as_str_without_rev())

        for opname, opdict in sorted(options.items()):
            if self.filter_string != "" and \
               self.filter_string not in opname:
                self.remove_option_widget(opname)
                continue
            ow = self.find_option_widget(opname)
            if ow is None:
                ow = self.add_option_widget(opname, opdict)
            ow.update()

        # MMCC TODO set filterbox.set_info

        for w, _ in self.pile.contents[2:]:
            w.update()
        self.sort_option_widgets()

    def handle_filter_change(self, edit_button, userdata):
        self.filter_string = userdata
        self.update()

    def handle_edit(self, opname, value):
        self.placement_controller.set_option(self.service.service_name,
                                             opname, value)

    def find_option_widget(self, opname):
        return next((ow for ow in self.option_widgets if
                     ow.name == opname), None)

    def add_option_widget(self, opname, opdict):
        cv = self.service.options.get(opname, None)
        ow = OptionWidget(opname,
                          opdict['Type'],
                          opdict['Description'],
                          opdict['Default'],
                          current_value=cv,
                          value_changed_callback=self.handle_edit)

        self.option_widgets.append(ow)
        self.pile.contents.append((ow,
                                   self.pile.options()))
        return ow

    def remove_option_widget(self, opname):
        ow = self.find_option_widget(opname)
        if ow is None:
            return

        self.option_widgets.remove(ow)
        ow_idx = 0
        for w, opts in self.pile.contents:
            if w == ow:
                break
            ow_idx += 1

        c = self.pile.contents[:ow_idx] + \
            self.pile.contents[ow_idx + 1:]
        self.pile.contents = c

    def focus_prev_or_top(self):
        # ? self.pile.focus_position = len(self.pile.contents) - 1

        if len(self.pile.contents) <= 2:
            return
        pos = self.pile.focus_position
        if pos < 2:
            self.pile.focus_position = 2

    def sort_option_widgets(self):
        def keyfunc(ow):
            return str(ow.name)
        self.option_widgets.sort(key=keyfunc)

        def wrappedkeyfunc(t):
            rw, options = t
            if isinstance(rw, OptionWidget):
                return keyfunc(rw)
            return 'A'

        self.pile.contents.sort(key=wrappedkeyfunc)
class JujuMachineWidget(WidgetWrap):

    """A widget displaying a machine and action buttons.

    juju_machine_id = juju id of the machine

    md = the machine dict

    application - the current application for which machines are being shown

    assign_cb - a function that takes a machine and assignmenttype to
    perform the button action

    unassign_cb - a function that takes a machine and removes
    assignments for the machine

    controller - a controller object that provides show_pin_chooser

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

    show_pin - show button to pin juju machine to a maas machine

    """

    def __init__(self, juju_machine_id, md, application, assign_cb,
                 unassign_cb, controller, show_assignments=True,
                 show_pin=False):
        self.juju_machine_id = juju_machine_id
        self.md = md
        self.application = application
        self.is_selected = False
        self.assign_cb = assign_cb
        self.unassign_cb = unassign_cb
        self.controller = controller
        self.show_assignments = show_assignments
        self.show_pin = show_pin
        self.all_assigned = False

        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def selectable(self):
        return True

    def __repr__(self):
        return "jujumachinewidget #" + str(self.juju_machine_id)

    def build_widgets(self):

        self.action_button_cols = Columns([])
        self.action_buttons = []
        self.build_unselected_widgets()
        self.pile = Pile([self.unselected_columns])
        return self.pile

    def build_unselected_widgets(self):
        cdict = juju.constraints_to_dict(self.md.get('constraints', ''))

        self.juju_machine_id_button = MenuSelectButton(
            '{:20s}'.format(self.juju_machine_id), self.show_pin_chooser)
        self.juju_machine_id_label = Text(
            "{:20s}".format(self.juju_machine_id))
        self.cores_field = IntEdit('', cdict.get('cores', ''))
        connect_signal(self.cores_field, 'change', self.handle_cores_changed)
        self.mem_field = Edit('', cdict.get('mem', ''))
        connect_signal(self.mem_field, 'change', self.handle_mem_changed)
        self.disk_field = Edit('', cdict.get('root-disk', ''))
        connect_signal(self.disk_field, 'change', self.handle_disk_changed)

        if self.show_pin:
            machine_id_w = self.juju_machine_id_button
        else:
            machine_id_w = self.juju_machine_id_label
        cols = [machine_id_w, self.cores_field,
                self.mem_field, self.disk_field]
        cols = [AttrMap(w, 'string_input',
                        'string_input_focus') for w in cols]
        cols.append(Text("placeholder"))
        self.unselected_columns = Columns(cols, dividechars=2)
        self.update_assignments()
        return self.unselected_columns

    def update_assignments(self):
        assignments = []

        mps = self.controller.get_all_assignments(self.juju_machine_id)
        if len(mps) > 0:
            if self.show_assignments:
                ad = defaultdict(list)
                for application, atype in mps:
                    ad[atype].append(application)
                astr = " ".join(["{}{}".format(
                    atype_to_label([atype])[0],
                    ",".join([application.service_name
                              for application in al]))
                    for atype, al in ad.items()])
                assignments.append(astr)
        else:
            if self.show_assignments:
                assignments.append("-")

        if any([application == self.application for application, _ in mps]):
            action = self.do_remove
            label = "Remove"
        else:
            action = self.do_select
            label = "Select"

        self.select_button = PlainButton(label, action)

        cols = [Text(s) for s in assignments]

        current_assignments = [a for a, _ in mps if a == self.application]
        if self.all_assigned and len(current_assignments) == 0:
            cols.append(Text(""))
        else:
            cols += [AttrMap(self.select_button, 'text',
                             'button_secondary focus')]
        opts = self.unselected_columns.options()
        self.unselected_columns.contents[4:] = [(w, opts) for w in cols]

    def update(self):
        self.update_action_buttons()

        if self.is_selected:
            self.update_selected()
        else:
            self.update_unselected()

    def update_selected(self):
        cn = self.application.service_name
        msg = Text("  Add {} to machine #{}:".format(cn,
                                                     self.juju_machine_id))
        self.pile.contents = [(msg, self.pile.options()),
                              (self.action_button_cols,
                               self.pile.options()),
                              (Divider(), self.pile.options())]

    def update_unselected(self):
        if self.show_pin:
            pinned_machine = self.controller.get_pin(self.juju_machine_id)

            if pinned_machine:
                pin_label = " {} \N{PENCIL}".format(pinned_machine.hostname)
            else:
                pin_label = " \N{PENCIL}"
            self.juju_machine_id_button.set_label('{:20s}'.format(
                self.juju_machine_id + " " + pin_label))
        else:
            self.juju_machine_id_label.set_text('{:20s}'.format(
                self.juju_machine_id))

        self.pile.contents = [(self.unselected_columns, self.pile.options()),
                              (Divider(), self.pile.options())]
        self.update_assignments()

    def update_action_buttons(self):

        all_actions = [(AssignmentType.BareMetal,
                        'Add as Bare Metal',
                        self.select_baremetal),
                       (AssignmentType.LXD,
                        'Add as LXD',
                        self.select_lxd),
                       (AssignmentType.KVM,
                        'Add as KVM',
                        self.select_kvm)]

        sc = self.application
        if sc:
            allowed_set = set(sc.allowed_assignment_types)
            allowed_types = set([atype for atype, _, _ in all_actions])
            allowed_types = allowed_types.intersection(allowed_set)
        else:
            allowed_types = set()

        # + 1 for the cancel button:
        if len(self.action_buttons) == len(allowed_types) + 1:
            return

        self.action_buttons = [AttrMap(PlainButton(label,
                                                   on_press=func),
                                       'button_secondary',
                                       'button_secondary focus')
                               for atype, label, func in all_actions
                               if atype in allowed_types]
        self.action_buttons.append(
            AttrMap(PlainButton("Cancel",
                                on_press=self.do_cancel),
                    'button_secondary',
                    'button_secondary focus'))

        opts = self.action_button_cols.options()
        self.action_button_cols.contents = [(b, opts) for b in
                                            self.action_buttons]

    def do_select(self, sender):
        self.is_selected = True
        self.update()
        self.pile.focus_position = 1
        self.action_button_cols.focus_position = 0

    def do_remove(self, sender):
        self.unassign_cb(self.juju_machine_id)

    def do_cancel(self, sender):
        self.is_selected = False
        self.update()
        self.pile.focus_position = 0

    def _do_select_assignment(self, atype):
        self.assign_cb(self.juju_machine_id, atype)
        self.pile.focus_position = 0
        self.is_selected = False
        self.update()

    def select_baremetal(self, sender):
        self._do_select_assignment(AssignmentType.BareMetal)

    def select_lxd(self, sender):
        self._do_select_assignment(AssignmentType.LXD)

    def select_kvm(self, sender):
        self._do_select_assignment(AssignmentType.KVM)

    def handle_cores_changed(self, sender, val):
        if val == '':
            self.md = self.controller.clear_constraint(self.juju_machine_id,
                                                       'cores')
        else:
            self.md = self.controller.set_constraint(self.juju_machine_id,
                                                     'cores', val)

    def _format_constraint(self, val):
        """Ensure that a constraint has a unit. bare numbers are treated as
        gigabytes"""
        try:
            return units.gb_to_human(float(val))
        except ValueError:
            return val

    def handle_mem_changed(self, sender, val):
        if val == '':
            self.md = self.controller.clear_constraint(self.juju_machine_id,
                                                       'mem')
        else:
            self.md = self.controller.set_constraint(
                self.juju_machine_id, 'mem',
                self._format_constraint(val))

    def handle_disk_changed(self, sender, val):
        if val == '':
            self.md = self.controller.clear_constraint(self.juju_machine_id,
                                                       'root-disk')
        else:
            self.md = self.controller.set_constraint(
                self.juju_machine_id, 'root-disk',
                self._format_constraint(val))

    def show_pin_chooser(self, sender):
        self.controller.show_pin_chooser(self.juju_machine_id)
class SimpleServiceWidget(WidgetWrap):

    """A widget displaying a service as a button

    service - the service to display

    placement_controller - a PlacementController instance

    display_controller - a PlacerView instance

    callback - a function to be called when either of the buttons is
    pressed. The service will be passed to the function as userdata.

    show_placements - display the machine(s) currently assigned to
    host this service, both planned deployments (aka 'assignments',
    and already-deployed, called 'deployments').

    """

    def __init__(self, service, placement_controller,
                 display_controller, show_placements=False):
        self.service = service
        self.placement_controller = placement_controller
        self.display_controller = display_controller
        self.show_placements = show_placements
        self.state = ServiceWidgetState.UNSELECTED
        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def selectable(self):
        return True

    def build_widgets(self):

        self.button = MenuSelectButton("I AM A SERVICE", self.do_select)

        self.action_button_cols = Columns([], dividechars=1)
        self.action_buttons = []

        self.pile = Pile([self.button])
        return self.pile

    def get_markup(self):
        if self.service.subordinate:
            return [self.service.service_name +
                    " (subordinate)\n  " +
                    self.service.charm_source], []

        nr = self.service.required_num_units()
        pl = "s" if nr > 1 else ""
        title_markup = [self.service.service_name +
                        "\n  {} unit{}: ".format(nr, pl) +
                        self.service.charm_source]
        info_markup = []

        if not self.display_controller.has_maas:
            return title_markup, info_markup

        pd = self.placement_controller.get_assignments(self.service)
        nplaced = sum([len(pd[k]) for k in pd])

        if nr-nplaced > 0:
            pl = ""
            if nr-nplaced > 1:
                pl = "s"
            info_str = ("  {} unit{} will be auto-placed "
                        "by Juju\n".format(nr-nplaced, pl))

            info_markup.append(info_str)

        def string_for_placement_dict(d):
            if self.display_controller.has_maas:
                defstring = "Bare Metal (Default)"
            else:
                defstring = "LXD (Default)"
            s = []
            for atype, ml in sorted(d.items()):
                if atype == AssignmentType.DEFAULT:
                    aname = defstring
                else:
                    aname = atype.name

                hostnames = [m.hostname for m in ml]
                s.append("    {}: {}".format(aname,
                                             ", ".join(hostnames)))
            if len(s) == 0:
                return []
            return "\n".join(s)

        ad = self.placement_controller.get_assignments(self.service)
        info_markup += string_for_placement_dict(ad)
        return title_markup, info_markup

    def update_choosing(self):
        title_markup, _ = self.get_markup()
        msg = Padding(Text(title_markup), left=2, right=2, align='center')
        self.pile.contents = [(msg, self.pile.options()),
                              (self.action_button_cols,
                               self.pile.options()),
                              (Divider(), self.pile.options())]

    def update_default(self):
        title_markup, info_markup = self.get_markup()
        self.button.set_label(title_markup + ["\n"] + info_markup)
        if self.state == ServiceWidgetState.SELECTED:
            b = AttrMap(self.button, 'deploy_highlight_start',
                        'button_secondary focus')
        else:
            b = AttrMap(self.button, 'text', 'button_secondary focus')

        self.pile.contents = [(b, self.pile.options()),
                              (Divider(), self.pile.options())]

    def update(self):
        self.service = next((s for s in
                             self.placement_controller.bundle.services
                             if s.service_name == self.service.service_name),
                            self.service)
        self.update_action_buttons()

        if self.state == ServiceWidgetState.CHOOSING:
            self.update_choosing()
        else:
            self.update_default()

    def keypress(self, size, key):
        if key == 'backspace':
            self.display_controller.remove_service(self.service)
        elif key == '+':
            if not self.service.subordinate:
                self.display_controller.scale_service(self.service, 1)
        elif key == '-':
            if not self.service.subordinate:
                self.display_controller.scale_service(self.service, -1)

        return super().keypress(size, key)

    def update_action_buttons(self):
        all_actions = []
        if self.display_controller.has_maas:
            all_actions = [('Choose Placement',
                            self.handle_placement_button_pressed)]
        all_actions += [('Edit Relations',
                         self.handle_relation_button_pressed),
                        ('Edit Options',
                         self.handle_options_button_pressed)]

        self.action_buttons = [AttrMap(PlainButton(label, on_press=func),
                                       'button_secondary',
                                       'button_secondary focus')
                               for label, func in all_actions]

        self.action_buttons.append(AttrMap(
            PlainButton("Cancel",
                        on_press=self.do_cancel),
            'button_secondary',
            'button_secondary focus'))

        opts = self.action_button_cols.options()
        self.action_button_cols.contents = [(b, opts) for
                                            b in self.action_buttons]

    def do_select(self, sender):
        self.display_controller.clear_selections()
        if self.state == ServiceWidgetState.SELECTED:
            self.state = ServiceWidgetState.UNSELECTED
            self.display_controller.set_selected_service(None)
        else:
            self.display_controller.set_selected_service(self.service)
            self.state = ServiceWidgetState.CHOOSING
            self.pile.focus_position = 1
            self.action_button_cols.focus_position = 0
        self.update()

    def do_cancel(self, sender):
        self.state = ServiceWidgetState.UNSELECTED
        self.update()
        self.display_controller.show_default_view()
        self.pile.focus_position = 0

    def handle_placement_button_pressed(self, sender):
        self.state = ServiceWidgetState.SELECTED
        self.update()
        self.display_controller.edit_placement()
        self.pile.focus_position = 0

    def handle_relation_button_pressed(self, sender):
        self.state = ServiceWidgetState.SELECTED
        self.update()
        self.display_controller.edit_relations()
        self.pile.focus_position = 0

    def handle_options_button_pressed(self, sender):
        self.state = ServiceWidgetState.SELECTED
        self.update()
        self.display_controller.edit_options()
        self.pile.focus_position = 0
Example #32
0
 def __init__(self, message, close_func):
     self.close_func = close_func
     button = Button("Close", self.do_close)
     box = LineBox(Pile([Text(message), button]), title="Info")
     super().__init__(box)
Example #33
0
class MachinesList(WidgetWrap):

    """A list of machines with configurable action buttons for each
    machine.

    action - a function to call when the machine's button is pressed

    constraints - a dict of constraints to filter the machines list.
    only machines matching all the constraints will be shown.

    show_hardware - bool, whether or not to show the hardware details
    for each of the machines

    title_widgets - A Text Widget to be used in place of the default
    title.

    show_assignments - bool, whether or not to show the assignments
    for each of the machines.

    show_only_ready - bool, only show machines with a ready state.

    """

    def __init__(self, controller, display_controller,
                 constraints=None, show_hardware=False,
                 title_widgets=None, show_assignments=True,
                 show_placeholders=True, show_only_ready=False,
                 show_filter_box=False):
        self.controller = controller
        self.display_controller = display_controller
        self.machine_widgets = []
        if constraints is None:
            self.constraints = {}
        else:
            self.constraints = constraints
        self.show_hardware = show_hardware
        self.show_assignments = show_assignments
        self.show_placeholders = show_placeholders
        self.show_only_ready = show_only_ready
        self.show_filter_box = show_filter_box
        self.filter_string = ""
        w = self.build_widgets(title_widgets)
        self.update()
        super().__init__(w)

    def selectable(self):
        # overridden to ensure that we can arrow through the buttons
        # shouldn't be necessary according to documented behavior of
        # Pile & Columns, but discovered via trial & error.
        return True

    def build_widgets(self, title_widgets):
        if title_widgets is None:
            if len(self.constraints) > 0:
                cstr = " matching constraints"
            else:
                cstr = ""

            title_widgets = [Text("Machines" + cstr, align='center')]

        self.filter_edit_box = FilterBox(self.handle_filter_change)

        header_widgets = title_widgets + [Divider()]

        if self.show_filter_box:
            header_widgets.append(self.filter_edit_box)

        self.header_padding = len(header_widgets)
        self.machine_pile = Pile(header_widgets + self.machine_widgets)
        return self.machine_pile

    def handle_filter_change(self, edit_button, userdata):
        self.filter_string = userdata
        self.update()

    def find_machine_widget(self, m):
        return next((mw for mw in self.machine_widgets if
                     mw.machine.instance_id == m.instance_id), None)

    def update(self):
        machines = self.controller.machines(
            include_placeholders=self.show_placeholders)
        if self.show_only_ready:
            machines = [m for m in machines
                        if m.status == MaasMachineStatus.READY]
        for mw in self.machine_widgets:
            machine = next((m for m in machines if
                            mw.machine.instance_id == m.instance_id), None)
            if machine is None:
                self.remove_machine(mw.machine)

        n_satisfying_machines = len(machines)

        def get_placement_filter_label(d):
            s = ""
            for atype, al in d.items():
                s += " ".join(["{} {}".format(cc.service_name,
                                              cc.display_name)
                               for cc in al])
            return s

        for m in machines:
            if not satisfies(m, self.constraints)[0]:
                self.remove_machine(m)
                n_satisfying_machines -= 1
                continue

            assignment_names = ""
            ad = self.controller.assignments_for_machine(m)
            assignment_names = get_placement_filter_label(ad)
            filter_label = "{} {}".format(m.filter_label(),
                                          assignment_names)
            if self.filter_string != "" and \
               self.filter_string not in filter_label:
                self.remove_machine(m)
                continue

            mw = self.find_machine_widget(m)
            if mw is None:
                mw = self.add_machine_widget(m)
            mw.update()

        self.filter_edit_box.set_info(len(self.machine_widgets),
                                      n_satisfying_machines)

        self.sort_machine_widgets()

    def add_machine_widget(self, machine):
        mw = SimpleMachineWidget(machine,
                                 self.controller,
                                 self.display_controller,
                                 self.show_assignments)
        self.machine_widgets.append(mw)
        options = self.machine_pile.options()
        self.machine_pile.contents.append((mw, options))

        # NOTE: see the +1: indexing in remove_machine if you re-add
        # this divider. it should then be +2.

        # self.machine_pile.contents.append((AttrMap(Padding(Divider('\u23bc'),
        #                                                    left=2, right=2),
        #                                            'label'), options))
        return mw

    def remove_machine(self, machine):
        mw = self.find_machine_widget(machine)
        if mw is None:
            return

        self.machine_widgets.remove(mw)
        mw_idx = 0
        for w, opts in self.machine_pile.contents:
            if w == mw:
                break
            mw_idx += 1

        c = self.machine_pile.contents[:mw_idx] + \
            self.machine_pile.contents[mw_idx + 1:]
        self.machine_pile.contents = c

    def sort_machine_widgets(self):
        def keyfunc(mw):
            m = mw.machine
            hwinfo = " ".join(map(str, [m.arch, m.cpu_cores, m.mem,
                                        m.storage]))
            if str(mw.machine.status) == 'ready':
                skey = 'A'
            else:
                skey = str(mw.machine.status)
            return skey + mw.machine.hostname + hwinfo
        self.machine_widgets.sort(key=keyfunc)

        def wrappedkeyfunc(t):
            mw, options = t
            if not isinstance(mw, SimpleMachineWidget):
                return 'A'
            return keyfunc(mw)

        self.machine_pile.contents.sort(key=wrappedkeyfunc)

    def focus_prev_or_top(self):
        self.update()
        try:
            if self.machine_pile.focus_position <= self.header_padding:
                self.machine_pile.focus_position = self.header_padding
        except IndexError:
            log.debug("index error in machines_list focus_top")
Example #34
0
 def __init__(self):
     self.text = Text(self.TITLE_TEXT)
     self.widget = Color.frame_header(self.text)
     self.pile = Pile([self.widget, Text("")])
     self.set_show_add_units_hotkey(False)
     super().__init__(self.pile)
Example #35
0
 def add(self, key, heading):
     self.columns[key] = Pile([Color.column_header(Text(heading))])
Example #36
0
class OptionsColumn(WidgetWrap):
    """UI to edit options of a service
    """
    def __init__(self, display_controller, placement_controller,
                 placement_view, metadata_controller):
        self.placement_controller = placement_controller
        self.metadata_controller = metadata_controller
        self.service = None
        self.filter_string = ""
        self.placement_view = placement_view

        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def build_widgets(self):
        self.title = Text('')
        self.option_widgets = []
        self.pile = Pile([Divider(), self.title] + self.option_widgets)
        return self.pile

    def refresh(self):
        self.set_service(self.service)

    def set_service(self, service):
        self.service = service
        self.metadata_controller.add_charm(service.csid.as_str_without_rev())
        self.pile.contents = self.pile.contents[:2]
        self.option_widgets = []

    def update(self):
        if self.service is None:
            return

        self.title.set_text(
            ('body', "Edit Options for {}".format(self.service.service_name)))

        if len(self.option_widgets) == 0:
            if self.filter_string != "":
                self.title.set_text(
                    ('body',
                     "No options match '{}'".format(self.filter_string)))
            else:
                self.title.set_text(('body', "Loading Options..."))
        else:
            self.title.set_text(('body', "Edit Options: (Changes are "
                                 "saved immediately)"))

        mc = self.metadata_controller
        options = mc.get_options(self.service.csid.as_str_without_rev())

        for opname, opdict in sorted(options.items()):
            if self.filter_string != "" and \
               self.filter_string not in opname:
                self.remove_option_widget(opname)
                continue
            ow = self.find_option_widget(opname)
            if ow is None:
                ow = self.add_option_widget(opname, opdict)
            ow.update()

        # MMCC TODO set filterbox.set_info

        for w, _ in self.pile.contents[2:]:
            w.update()
        self.sort_option_widgets()

    def handle_filter_change(self, edit_button, userdata):
        self.filter_string = userdata
        self.update()

    def handle_edit(self, opname, value):
        self.placement_controller.set_option(self.service.service_name, opname,
                                             value)

    def find_option_widget(self, opname):
        return next((ow for ow in self.option_widgets if ow.name == opname),
                    None)

    def add_option_widget(self, opname, opdict):
        cv = self.service.options.get(opname, None)
        ow = OptionWidget(opname,
                          opdict['Type'],
                          opdict['Description'],
                          opdict['Default'],
                          current_value=cv,
                          value_changed_callback=self.handle_edit)

        self.option_widgets.append(ow)
        self.pile.contents.append((ow, self.pile.options()))
        return ow

    def remove_option_widget(self, opname):
        ow = self.find_option_widget(opname)
        if ow is None:
            return

        self.option_widgets.remove(ow)
        ow_idx = 0
        for w, opts in self.pile.contents:
            if w == ow:
                break
            ow_idx += 1

        c = self.pile.contents[:ow_idx] + \
            self.pile.contents[ow_idx + 1:]
        self.pile.contents = c

    def focus_prev_or_top(self):
        # ? self.pile.focus_position = len(self.pile.contents) - 1

        if len(self.pile.contents) <= 2:
            return
        pos = self.pile.focus_position
        if pos < 2:
            self.pile.focus_position = 2

    def sort_option_widgets(self):
        def keyfunc(ow):
            return str(ow.name)

        self.option_widgets.sort(key=keyfunc)

        def wrappedkeyfunc(t):
            rw, options = t
            if isinstance(rw, OptionWidget):
                return keyfunc(rw)
            return 'A'

        self.pile.contents.sort(key=wrappedkeyfunc)
 def build_widgets(self):
     self.service_pile = Pile(
         [Text(self.title), Divider(' ')] + self.service_widgets)
     return self.service_pile
Example #38
0
class CloudView(WidgetWrap):
    def __init__(self, app, public_clouds, custom_clouds, cb=None):
        self.app = app
        self.cb = cb
        self.public_clouds = public_clouds
        self.custom_clouds = custom_clouds
        self.config = self.app.config
        self.buttons_pile_selected = False
        self.pile = None
        self.pile_localhost_idx = None

        self.frame = Frame(body=Padding.center_80(
            Filler(self._build_widget(), valign='top')),
                           footer=self._build_footer())
        super().__init__(self.frame)

    def keypress(self, size, key):
        if key in ['tab', 'shift tab']:
            self._swap_focus()
        return super().keypress(size, key)

    def _swap_focus(self):
        if not self.buttons_pile_selected:
            self.buttons_pile_selected = True
            self.frame.focus_position = 'footer'
            self.buttons_pile.focus_position = 1
        else:
            self.buttons_pile_selected = False
            self.frame.focus_position = 'body'

    def _build_buttons(self):
        cancel = menu_btn(on_press=self.cancel, label="\n  QUIT\n")
        buttons = [
            Padding.line_break(""),
            Color.menu_button(cancel, focus_map='button_primary focus'),
        ]
        self.buttons_pile = Pile(buttons)
        return self.buttons_pile

    def _build_footer(self):
        footer_pile = Pile([
            Padding.line_break(""),
            Color.frame_footer(
                Columns([('fixed', 2, Text("")),
                         ('fixed', 13, self._build_buttons())]))
        ])
        return footer_pile

    def _get_localhost_widget_idx(self):
        """ Returns index in pile where localhost widget resides
        """
        if self.pile_localhost_idx:
            return self.pile_localhost_idx

        else:
            for idx, item in enumerate(self.pile.contents):
                if hasattr(item[0], 'original_widget') and \
                   isinstance(item[0].original_widget, MenuSelectButton) and \
                   item[0].original_widget.get_label() == 'localhost':
                    return idx

    def _enable_localhost_widget(self):
        """ Sets the proper widget for localhost availability
        """
        idx = self._get_localhost_widget_idx()
        widget = Color.body(menu_btn(label=cloud_types.LOCALHOST,
                                     on_press=self.submit),
                            focus_map='menu_button focus')
        self._update_pile_at_idx(idx, widget)
        del self.pile.contents[idx + 1]

    def _update_pile_at_idx(self, idx, item):
        """ In place updates a widget in the self.pile contents
        """
        self.pile_localhost_idx = idx
        self.pile.contents[idx] = (item, self.pile.options())

    def _add_item(self, item):
        if not self.pile:
            self.pile = Pile([item])
        else:
            self.pile.contents.append((item, self.pile.options()))

    def _build_widget(self):
        if len(self.public_clouds) > 0:
            self._add_item(Text("Public Clouds"))
            self._add_item(HR())
            for item in self.public_clouds:
                self._add_item(
                    Color.body(menu_btn(label=item, on_press=self.submit),
                               focus_map='menu_button focus'))
            self._add_item(Padding.line_break(""))
        if len(self.custom_clouds) > 0:
            self._add_item(Text("Your Clouds"))
            self._add_item(HR())
            for item in self.custom_clouds:
                self._add_item(
                    Color.body(menu_btn(label=item, on_press=self.submit),
                               focus_map='menu_button focus'))
            self._add_item(Padding.line_break(""))
        new_clouds = juju.get_compatible_clouds(
            ['localhost', 'maas', 'vsphere'])
        if new_clouds:
            self._add_item(Text("Configure a New Cloud"))
            self._add_item(HR())
            for item in sorted(new_clouds):
                if item == 'localhost':
                    self._add_item(
                        Color.info_context(menu_btn(
                            label=cloud_types.LOCALHOST, on_press=None),
                                           focus_map='disabled_button'))
                    self._add_item(
                        Color.info_context(
                            Padding.center_90(
                                Text("LXD not found, please install with "
                                     "`sudo snap install lxd && lxd init` "
                                     "and wait for this message to disappear.")
                            )))
                else:
                    self._add_item(
                        Color.body(menu_btn(label=item, on_press=self.submit),
                                   focus_map='menu_button focus'))

        self.pile.focus_position = 2
        return self.pile

    def submit(self, result):
        self.cb(result.label)

    def cancel(self, btn):
        EventLoop.exit(0)
class MachineWaitView(WidgetWrap):

    def __init__(self, display_controller, installer, config):
        self.display_controller = display_controller
        self.installer = installer
        self.config = config
        creds = self.config.getopt('maascreds')
        if os.getenv("FAKE_API_DATA"):
            self.maas_client = None
            self.maas_state = FakeMaasState()
        else:
            self.maas_client, self.maas_state = connect_to_maas(creds)
        self.spinner = Spinner(15, 4)
        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def build_widgets(self):
        self.message = Text("Please review available machines in MAAS",
                            align='center')

        self.button_pile = Pile([])

        self.main_pile = Pile([self.message,
                               Divider(),
                               self.button_pile])
        return Filler(self.main_pile, valign='middle')

    def keypress(self, size, key):
        key = {'tab': 'down', 'shift tab': 'up'}.get(key, key)
        return super().keypress(size, key)

    def scroll_down(self):
        pass

    def scroll_up(self):
        pass

    def selectable(self):
        return True

    def get_status(self):
        " returns (global_ok, [ok, condition])"
        self.maas_state.invalidate_nodes_cache()
        machines = self.maas_state.machines(state=MaasMachineStatus.READY)
        powerable_machines = [m for m in machines if m.power_type is not None]
        n_powerable = len(powerable_machines)

        conditions = [(n_powerable >= 1,
                       "At least one machine enlisted with power "
                       "control (currently {})".format(n_powerable))]

        global_ok = all([ok for ok, _ in conditions])
        return global_ok, conditions

    def update(self):
        msg = ("Before continuing, ensure that at least one machine is "
               "enlisted into MAAS:")
        self.message = Text(self.spinner.next_frame() + ['\n', msg, '\n'],
                            align='center')
        contents = [(self.message,
                     self.main_pile.options())]

        global_ok, statuses = self.get_status()
        status_map = {True: ('success_icon', "\u2713 "),
                      False: ('error_icon', "<!> ")}
        contents += [(Text([status_map[status], condition],
                           align='center'),
                      self.main_pile.options())
                     for status, condition
                     in statuses]
        contents += [(Divider(), self.main_pile.options()),
                     (self.button_pile, self.main_pile.options())]
        self.main_pile.contents = contents

        if not global_ok:
            b = AttrMap(SelectableIcon(" ( Can't Continue ) "),
                        'disabled_button', 'disabled_button_focus')
        else:
            b = AttrMap(Button("Continue",
                               on_press=self.do_continue),
                        'button_primary', 'button_primary focus')

        cancel_b = AttrMap(Button("Cancel",
                                  on_press=self.do_cancel),
                           'button_secondary',
                           'button_secondary focus')
        self.button_pile.contents = [(Padding(cancel_b, width=24,
                                              align='center'),
                                      self.button_pile.options()),
                                     (Padding(b, width=24, align='center'),
                                      self.button_pile.options())]

        # ensure that the button is always focused:
        self.main_pile.focus_position = len(self.main_pile.contents) - 1

    @utils.async
    def do_continue(self, *args, **kwargs):
        self.installer.do_install()

    def do_cancel(self, *args, **kwargs):
        raise SystemExit("Installation cancelled.")
Example #40
0
 def _build_model_inputs(self):
     sl = [
         Columns([("weight", 0.2, Text("Your name:", align="right")),
                  ("weight", 0.3,
                   Color.string_input(self.realname,
                                      focus_map="string_input focus"))],
                 dividechars=4),
         Columns(
             [("weight", 0.2, Text("Your server's name:", align="right")),
              ("weight", 0.3,
               Color.string_input(self.hostname,
                                  focus_map="string_input focus"))],
             dividechars=4),
         Columns([
             ("weight", 0.2, Text("", align="right")),
             ("weight", 0.3,
              Color.info_minor(
                  Text(
                      "The name it uses when it talks to "
                      "other computers",
                      align="left"))),
         ],
                 dividechars=4),
         Columns([("weight", 0.2, Text("Pick a username:"******"right")),
                  ("weight", 0.3,
                   Color.string_input(self.username,
                                      focus_map="string_input focus"))],
                 dividechars=4),
         Columns(
             [("weight", 0.2, Text("Choose a password:"******"right")),
              ("weight", 0.3,
               Color.string_input(self.password,
                                  focus_map="string_input focus"))],
             dividechars=4),
         Columns([
             ("weight", 0.2, Text("Confirm your password:"******"right")),
             ("weight", 0.3,
              Color.string_input(self.confirm_password,
                                 focus_map="string_input focus"))
         ],
                 dividechars=4),
         Columns(
             [("weight", 0.2, Text("Import SSH identity:", align="right")),
              ("weight", 0.3,
               Color.string_input(self.ssh_import_id,
                                  focus_map="string_input focus"))],
             dividechars=4),
         Columns([
             ("weight", 0.2, Text("", align="right")),
             ("weight", 0.3,
              Color.info_minor(
                  Text(
                      "Input your SSH user id from "
                      "Ubuntu SSO (sso:email), "
                      "Launchpad (lp:username) or "
                      "Github (gh:username).",
                      align="left"))),
         ],
                 dividechars=4),
     ]
     return Pile(sl)
Example #41
0
 def _display_schedule(self, s, f, date, schedule=None):
     columns = list(self.__schedule_fields(s, f, date, schedule))
     if columns:
         yield Pile([Text('Monitor'), Indent(Pile(columns))])
Example #42
0
 def build_widgets(self):
     self.title = Text('')
     self.option_widgets = []
     self.pile = Pile([Divider(), self.title] + self.option_widgets)
     return self.pile
Example #43
0
 def _create_text(self):
     text = []
     for line in self.BANNER:
         text.append(Text(line, align='center'))
     return Pile(text)
Example #44
0
 def build_widgets(self):
     self.title = Text('')
     self.pile = Pile([self.title])
     return self.pile
class NetworkSetDefaultRouteView(BaseView):
    def __init__(self, model, family, signal):
        self.model = model
        self.family = family
        self.signal = signal
        self.default_gateway_w = None
        self.gateway_options = Pile(self._build_default_routes())
        body = [
            Padding.center_79(Text("Please set the default gateway:")),
            Padding.line_break(""),
            Padding.center_79(self.gateway_options),
            Padding.line_break(""),
            Padding.fixed_10(self._build_buttons())
        ]
        super().__init__(ListBox(body))

    def _build_default_routes(self):
        ''' iterate through interfaces collecting
            any uniq provider (aka, gateway) and
            associate the interface name with the gateway

            then generate a line per key in the gateway
            dict and display the keys.

            Upon selection of the gateway entry (ip)
            then we set model.set_default_gateway(ip)

            if manual is selected, then we update
            the second entry into a IPAddressEditor
            and accept the value, submitting it to
            the model.
        '''
        providers = {}

        for iface in self.model.get_all_interfaces():
            if self.family == netifaces.AF_INET:
                ip_providers = iface.ip4_providers
            elif self.family == netifaces.AF_INET6:
                ip_providers = iface.ip6_providers

            for provider in ip_providers:
                log.debug('ipv4 provider: {}'.format(provider))
                gw = provider
                if gw in providers:
                    providers[gw].append(iface.ifname)
                else:
                    providers[gw] = [iface.ifname]

        log.debug('gateway providers: {}'.format(providers))
        items = []
        items.append(
            Padding.center_79(
                Color.menu_button(menu_btn(label="None", on_press=self.done),
                                  focus_map="menu_button focus")))
        for (gw, ifaces) in providers.items():
            if gw is None:
                continue
            items.append(
                Padding.center_79(
                    Color.menu_button(menu_btn(label="{gw} ({ifaces})".format(
                        gw=gw, ifaces=(",".join(ifaces))),
                                               on_press=self.done),
                                      focus_map="menu_button focus")))

        items.append(
            Padding.center_79(
                Color.menu_button(menu_btn(
                    label="Specify the default route manually",
                    on_press=self.show_edit_default_route),
                                  focus_map="menu_button focus")))
        return items

    def _build_buttons(self):
        cancel = cancel_btn(on_press=self.cancel)
        done = done_btn(on_press=self.done)

        buttons = [
            Color.button(done, focus_map='button focus'),
            Color.button(cancel, focus_map='button focus')
        ]
        return Pile(buttons)

    def show_edit_default_route(self, btn):
        log.debug("Re-rendering specify default route")
        self.default_gateway_w = StringEditor(
            caption="Default gateway will be ")
        self.gateway_options.contents[-1] = (Padding.center_50(
            Color.string_input(self.default_gateway_w,
                               focus_map="string_input focus")),
                                             self.gateway_options.options())
        # self.signal.emit_signal('refresh')

    def done(self, result):
        log.debug("changing default gw: {}".format(result))

        gw_func = None
        if self.family == netifaces.AF_INET:
            gw_func = self.model.set_default_v4_gateway
        elif self.family == netifaces.AF_INET6:
            gw_func = self.model.set_default_v6_gateway

        if self.default_gateway_w and self.default_gateway_w.value:
            try:
                gw_func(None, self.default_gateway_w.value)
            except ValueError:
                # FIXME: raise UX error message
                self.default_gateway_w.edit_text = ""
        else:
            gw_ip_from_label = result.label.split(" ")[0]
            log.debug("default gw entered: {}".format(gw_ip_from_label))
            try:
                if gw_ip_from_label.startswith('None'):
                    gw_func(None, None)
                else:
                    gw_func(None, gw_ip_from_label)
            except ValueError:
                # FIXME: raise UX error message
                pass
        self.signal.prev_signal()

    def cancel(self, button):
        self.signal.prev_signal()
Example #46
0
class CharmstoreColumn(WidgetWrap):

    def __init__(self, display_controller, placement_controller,
                 placement_view, metadata_controller):
        self.placement_controller = placement_controller
        self.display_controller = display_controller
        self.placement_view = placement_view
        self.metadata_controller = metadata_controller
        self.state = CharmstoreColumnUIState.RELATED
        self.prev_state = None
        self.current_search_string = ""
        w = self.build_widgets()
        super().__init__(w)
        self._related_charms = []
        self._bundle_results = []
        self._charm_results = []
        self._recommended_charms = []
        self.loading = True
        self.update()

    def build_widgets(self):
        self.title = Text('')
        self.pile = Pile([self.title])
        return self.pile

    def clear_search_results(self):
        self._bundle_results = []
        self._charm_results = []

    def handle_search_change(self, s):
        if s == "":
            self.state = CharmstoreColumnUIState.RELATED
            self.clear_search_results()
        else:
            self.state = CharmstoreColumnUIState.SEARCH_RESULTS
        self.current_search_string = s
        self.loading = True
        self.update()

    def remove_existing_charms(self, charms):
        existing_charms = [CharmStoreID(s.charm_source).as_str_without_rev()
                           for s in
                           self.placement_controller.bundle.services]
        return [c for c in charms
                if CharmStoreID(c['Id']).as_str_without_rev()
                not in existing_charms]

    def get_filtered_recommendations(self):
        opts = self.pile.options()
        if len(self._recommended_charms) == 0:
            mc = self.metadata_controller
            self._recommended_charms = mc.get_recommended_charms()
        return [(CharmWidget(d, self.do_add_charm, recommended=True), opts)
                for d in self.remove_existing_charms(self._recommended_charms)]

    def update(self):
        opts = self.pile.options()
        extra_widgets = []
        recommended_widgets = []
        if self.metadata_controller.loaded():
            recommended_widgets = self.get_filtered_recommendations()
            if len(recommended_widgets) > 0:
                top_w = recommended_widgets[0][0]
                top_w.set_header("Recommended Charms")

            self.loading = False

        series = self.placement_controller.bundle.series
        bundle_widgets = [(BundleWidget(d, self.do_add_bundle),
                           opts) for d in self._bundle_results
                          if 'bundle-metadata' in d.get('Meta', {}) and
                          'Series' in d['Meta']['bundle-metadata'] and
                          d['Meta']['bundle-metadata']['Series'] == series]

        if len(bundle_widgets) > 0:
            top_w = bundle_widgets[0][0]
            top_w.set_header("Bundles")

        filtered_charm_results = self.remove_existing_charms(
            self._charm_results)
        filtered_charm_results = filtered_charm_results[:10]

        charm_widgets = [(CharmWidget(d, self.do_add_charm),
                          opts) for d in filtered_charm_results
                         if 'charm-metadata' in d.get('Meta', {})]
        if len(charm_widgets) > 0:
            top_w = charm_widgets[0][0]
            top_w.set_header("Charms")

        if self.state == CharmstoreColumnUIState.RELATED:
            if self.loading:
                self.title.set_text("\nLoading Recommended "
                                    "and Popular Charms…")
            else:
                self.title.set_text("")
            extra_widgets = recommended_widgets
        else:
            if self.loading:
                msg = "\nSearching for '{}'…\n".format(
                    self.current_search_string)
                self.title.set_text(msg)
            else:
                bn = len(self._bundle_results)
                cn = len(filtered_charm_results)
                if bn + cn == 0:
                    advice = ""
                    if len(self.current_search_string) < 3:
                        advice = "Try a longer search string."
                    msg = ("\nNo charms found matching '{}' "
                           "{}".format(self.current_search_string,
                                       advice))
                else:
                    msg = ("\nShowing the top {} bundles and {} "
                           "charms matching {}:"
                           "".format(bn, cn, self.current_search_string))
            self.title.set_text(msg)

        self.pile.contents[1:] = extra_widgets + bundle_widgets + charm_widgets

    def add_results(self, bundle_results, charm_results):
        self._bundle_results += bundle_results
        self._bundle_results = self._bundle_results[:5]
        self._charm_results += charm_results

    def focus_prev_or_top(self):
        if len(self.pile.contents) < 2:
            return
        if self.state == CharmstoreColumnUIState.RELATED and self.loading \
           and len(self.pile.contents) > 2:
            self.pile.focus_position = 2
        else:
            self.pile.focus_position = 1

    def do_add_charm(self, charm_name, charm_dict):
        self.placement_view.do_add_charm(charm_name,
                                         charm_dict)
        self.update()

    def do_add_bundle(self, bundle_dict):
        self.placement_view.do_add_bundle(bundle_dict)

    def handle_error(self, e):
        msg = "Error searching for {}: {}".format(self.current_search_string,
                                                  e)
        self.title.set_text(msg)
Example #47
0
from urwid import MainLoop, SolidFill, Filler, Pile, Overlay, LineBox

interior = Filler(Pile([]))

window = LineBox(interior, title='Choose enviroment')
background = SolidFill(' ')

body = Overlay(window, background, 'center', 30, 'middle', 10)

main_loop = MainLoop(body)
main_loop.run()
Example #48
0
 def __init__(self):
     self.title_widget = AttrWrap(padding(Text(TITLE_TEXT)),
                                  "header_title")
     self.pile = Pile([self.title_widget, Text("")])
     self.set_show_add_units_hotkey(False)
     super().__init__(self.pile)
Example #49
0
 def _display_date(self, s, f, date):
     columns = self.__fields(s, date)
     if columns:
         yield Pile([Text('Monitor'), Indent(Columns(columns))])
Example #50
0
 def build_info(self):
     items = [Text(self.msg)]
     return Pile(items)
class ServicesList(WidgetWrap):
    """A list of services (charm classes) with flexible display options.

    Note that not all combinations of display options make sense. YMMV.

    controller - a PlacementController

    actions - a list of tuples describing buttons. Passed to
    ServiceWidget.

    machine - a machine instance to query for constraint checking. If
    None, no constraint checking is done. If set, only services whose
    constraints are satisfied by 'machine' are shown.

    ignore_assigned - bool, whether or not to display services that
    have already been assigned to a machine (but not yet deployed)

    ignore_deployed - bool, whether or not to display services that
    have already been deployed

    deployed_only - bool, only show deployed services

    show_constraints - bool, whether or not to display the constraints
    for the various services

    show_type - string, one of 'all', 'required' or 'non-required',
    controls which charm states should be shown. default is 'all'.

    trace_updates - bool, enable verbose update logging

    """
    def __init__(self,
                 controller,
                 actions,
                 subordinate_actions,
                 machine=None,
                 ignore_assigned=False,
                 ignore_deployed=False,
                 assigned_only=False,
                 deployed_only=False,
                 show_type='all',
                 show_constraints=False,
                 show_placements=False,
                 title="Services",
                 trace_updates=False):
        self.controller = controller
        self.actions = actions
        self.subordinate_actions = subordinate_actions
        self.service_widgets = []
        self.machine = machine
        self.ignore_assigned = ignore_assigned
        self.ignore_deployed = ignore_deployed
        self.assigned_only = assigned_only
        self.deployed_only = deployed_only
        self.show_type = show_type
        self.show_constraints = show_constraints
        self.show_placements = show_placements
        self.title = title
        self.trace = trace_updates
        w = self.build_widgets()
        self.update()
        super().__init__(w)

    def selectable(self):
        # overridden to ensure that we can arrow through the buttons
        # shouldn't be necessary according to documented behavior of
        # Pile & Columns, but discovered via trial & error.
        return True

    def build_widgets(self):
        self.service_pile = Pile(
            [Text(self.title), Divider(' ')] + self.service_widgets)
        return self.service_pile

    def find_service_widget(self, cc):
        return next((sw for sw in self.service_widgets
                     if sw.charm_class.charm_name == cc.charm_name), None)

    def update(self):
        def trace(cc, s):
            if self.trace:
                log.debug("{}: {} {}".format(self.title, cc, s))

        for cc in self.controller.charm_classes():
            if self.machine:
                if not satisfies(self.machine, cc.constraints)[0] \
                   or not (self.controller.is_assigned_to(cc, self.machine) or
                           self.controller.is_deployed_to(cc, self.machine)):
                    self.remove_service_widget(cc)
                    trace(cc, "removed because machine doesn't match")
                    continue

            if self.ignore_assigned and self.assigned_only:
                raise Exception("Can't both ignore and only show assigned.")

            if self.ignore_assigned:
                n = self.controller.assignment_machine_count_for_charm(cc)
                if n == cc.required_num_units() \
                   and not cc.allow_multi_units \
                   and self.controller.is_assigned(cc):
                    self.remove_service_widget(cc)
                    trace(cc, "removed because max units are " "assigned")
                    continue
            elif self.assigned_only:
                if not self.controller.is_assigned(cc):
                    self.remove_service_widget(cc)
                    trace(
                        cc, "removed because it is not assigned and "
                        "assigned_only is True")
                    continue

            if self.ignore_deployed and self.deployed_only:
                raise Exception("Can't both ignore and only show deployed.")

            if self.ignore_deployed:
                n = self.controller.deployment_machine_count_for_charm(cc)
                if n == cc.required_num_units() \
                   and self.controller.is_deployed(cc):
                    self.remove_service_widget(cc)
                    trace(
                        cc, "removed because the required number of units"
                        " has been deployed")
                    continue
            elif self.deployed_only:
                if not self.controller.is_deployed(cc):
                    self.remove_service_widget(cc)
                    continue

            state, _, _ = self.controller.get_charm_state(cc)
            if self.show_type == 'required':
                if state != CharmState.REQUIRED:
                    self.remove_service_widget(cc)
                    continue
            elif self.show_type == 'non-required':
                if state == CharmState.REQUIRED:
                    self.remove_service_widget(cc)
                    trace(
                        cc, "removed because show_type is 'non-required' and"
                        "state is REQUIRED.")
                    continue
                assigned_or_deployed = (self.controller.is_assigned(cc)
                                        or self.controller.is_deployed(cc))
                if not cc.allow_multi_units and assigned_or_deployed:
                    self.remove_service_widget(cc)
                    trace(
                        cc, "removed because it doesn't allow multiple units"
                        " and is not assigned or deployed.")
                    continue

            sw = self.find_service_widget(cc)
            if sw is None:
                sw = self.add_service_widget(cc)
                trace(cc, "added widget")
            sw.update()

    def add_service_widget(self, charm_class):
        if charm_class.subordinate:
            actions = self.subordinate_actions
        else:
            actions = self.actions
        sw = ServiceWidget(charm_class,
                           self.controller,
                           actions,
                           self.show_constraints,
                           show_placements=self.show_placements)
        self.service_widgets.append(sw)
        options = self.service_pile.options()
        self.service_pile.contents.append((sw, options))
        self.service_pile.contents.append(
            (AttrMap(Padding(Divider('\u23bc'), left=2, right=2),
                     'label'), options))
        return sw

    def remove_service_widget(self, charm_class):
        sw = self.find_service_widget(charm_class)

        if sw is None:
            return
        self.service_widgets.remove(sw)
        sw_idx = 0
        for w, opts in self.service_pile.contents:
            if w == sw:
                break
            sw_idx += 1

        c = self.service_pile.contents[:sw_idx] + \
            self.service_pile.contents[sw_idx + 2:]
        self.service_pile.contents = c