예제 #1
0
    def build_widgets(self, maxlen):
        num_str = "{}".format(self.application.num_units)
        col_pad = 6
        self.unit_w = Text('Units: {:4d}'.format(self.application.num_units),
                           align='right')
        cws = [
            (maxlen + col_pad, Text(self.application.service_name)),
            (10 + len(num_str), self.unit_w),
            # placeholder for instance type
            ('weight', 1, Text(" ")),
            # placeholder for configure button
            ('weight', 1, Text(" ")),
            (20,
             Color.button_primary(PlainButton(
                 "Deploy", partial(self.deploy_cb, self.application)),
                                  focus_map='button_primary focus'))
        ]
        if not self.hide_config:
            cws[3] = (20,
                      Color.button_secondary(
                          PlainButton(
                              "Configure",
                              partial(self.controller.do_configure,
                                      self.application)),
                          focus_map='button_secondary focus'))

        self.columns = Columns(cws, dividechars=1)
        return self.columns
예제 #2
0
    def update_action_buttons(self):

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

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

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

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

        opts = self.action_button_cols.options()
        self.action_button_cols.contents = [(b, opts)
                                            for b in self.action_buttons]
예제 #3
0
    def build_widgets(self):
        ws = [Text("{} Applications in {}:".format(len(self.applications),
                                                   app.config['spell']))]
        max_app_name_len = max([len(a.service_name) for a in
                                self.applications])

        for a in self.applications:
            ws.append(Text(""))
            ws.append(ApplicationWidget(a, max_app_name_len,
                                        self.controller,
                                        self.do_deploy))

        self.description_w = Text("App description")
        ws += [HR(), self.description_w]

        self.skip_rest_button = PlainButton(
            "Deploy all",
            self.controller.do_deploy_remaining
        )
        cws = [('weight', 1, Text(" ")),
               (20, Color.button_secondary(
                   self.skip_rest_button,
                   focus_map='button_secondary focus'))]
        self.button_columns = Columns(cws, dividechars=1)

        ws += [HR(), self.button_columns]
        self.pile = Pile(ws)
        return Padding.center_90(Filler(self.pile, valign="top"))
예제 #4
0
 def build_widgets(self):
     ws = [Text("Configure {}".format(self.application.service_name))]
     ws += self.get_option_widgets()
     ws += [
         HR(),
         PlainButton("Cancel", self.do_cancel),
         PlainButton("Accept Changes", self.do_commit)
     ]
     self.pile = Pile(ws)
     return Padding.center_90(Filler(self.pile, valign="top"))
예제 #5
0
    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"))
예제 #6
0
    def build_widgets(self):
        title_text = Text([("body", self.name)],
                          align="center")

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

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

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

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

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

        return Pile([Divider(), title_text, desc_text, self.control,
                     button_grid])
예제 #7
0
    def build_widgets(self):
        readme_files = glob(os.path.join(self.spell_dir, 'README.*'))
        if len(readme_files) == 0:
            self.readme_w = Text("No README found for bundle.")
        else:
            readme_file = readme_files[0]
            if len(readme_files) != 1:
                utils.warning("Unexpected: {} files matching README.*"
                              "- using {}".format(len(readme_files),
                                                  readme_file))
            with open(readme_file) as rf:
                rlines = [Text(l) for l in rf.readlines()]
                self.readme_w = BoxAdapter(ListBox(rlines),
                                           self.initial_height)

        ws = [
            Text("About {}:".format(self.spell_name)),
            Padding.right_50(
                Color.button_primary(PlainButton("Continue", self.do_continue),
                                     focus_map='button_primary focus')),
            Padding.center(HR()),
            Padding.center(self.readme_w, left=2),
            Padding.center(HR()),
            Padding.center(
                Text("Use arrow keys to scroll text "
                     "and TAB to select the button."))
        ]

        self.pile = Pile(ws)
        return Padding.center_90(Filler(self.pile, valign="top"))
예제 #8
0
    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
예제 #9
0
    def get_machines_header(self, machines_column):
        b = PlainButton("Open in Browser", on_press=self.browse_maas)
        self.open_maas_button = AttrMap(b, 'button_secondary',
                                        'button_secondary focus')
        self.maastitle = Text("Connected to MAAS")
        maastitle_widgets = Padding(Columns(
            [self.maastitle, (22, self.open_maas_button)]),
                                    align='center',
                                    width='pack',
                                    left=2,
                                    right=2)

        f = machines_column.machines_list.handle_filter_change
        self.filter_edit_box = FilterBox(f)
        pl = [
            Divider(),
            Text(('body', "Ready Machines {}".format(
                MetaScroll().get_text()[0])),
                 align='center'),
            Divider(), maastitle_widgets,
            Divider(), self.filter_edit_box
        ]

        self.machines_header_pile = Pile(pl)
        return self.machines_header_pile
예제 #10
0
 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_option_widgets()
     ws += [
         HR(),
         PlainButton("Cancel", self.do_cancel),
         PlainButton("Accept Changes", self.do_commit)
     ]
     self.pile = Pile(ws)
     return Padding.center_90(Filler(self.pile, valign="top"))
예제 #11
0
    def build_widgets(self):
        m = self.machine
        l = [
            '{:20s}'.format(m.hostname), '{:3d}'.format(m.cpu_cores),
            '{:6s}'.format(m.mem), '{:10s}'.format(m.storage)
        ]

        self.select_button_label = "Pin {} to {}".format(
            m.hostname, self.target_info)
        self.unselect_button_label = "Un-pin {} from ".format(
            m.hostname) + "{}"

        self.select_button = PlainButton(self.select_button_label,
                                         self.handle_button)
        cols = [Text(m) for m in l]
        cols += [AttrMap(self.select_button, 'text', 'button_secondary focus')]

        self.columns = Columns(cols)
        return self.columns
예제 #12
0
    def _simple_header_widgets(self, title):
        b = PlainButton("Back to Charm Store", on_press=self.show_default_view)
        self.back_to_mainview_button = AttrMap(b, 'button_secondary',
                                               'button_secondary focus')
        button_grid = GridFlow([self.back_to_mainview_button], 36, 1, 0,
                               'center')

        return [
            Divider(),
            Text(('body', title), align='center'),
            Divider(), button_grid
        ]
예제 #13
0
    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]
예제 #14
0
 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.num_units_copy,
                                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"))
예제 #15
0
    def get_services_header(self):
        b = PlainButton("Clear All Placements", on_press=self.do_clear_all)
        self.clear_all_button = AttrMap(b, 'button_secondary',
                                        'button_secondary focus')

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

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

        return Pile(ws)
예제 #16
0
    def build_widgets(self):
        desc_text = Text(["\n", strip_solo_dots(self.description)])

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

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

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

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

        return Pile([
            Padding.line_break(""),
            Padding.left(self.control_columns, left=1),
            Padding.left(desc_text, left=2), button_grid
        ])
예제 #17
0
 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"))
예제 #18
0
    def build_widgets(self):
        m = self.machine
        l = ['{:20s}'.format(m.hostname),
             '{:3d}'.format(m.cpu_cores),
             '{:6s}'.format(m.mem),
             '{:10s}'.format(m.storage)]

        self.select_button_label = "Pin {} to {}".format(m.hostname,
                                                         self.target_info)
        self.unselect_button_label = "Un-pin {} from ".format(
            m.hostname) + "{}"

        self.select_button = PlainButton(self.select_button_label,
                                         self.handle_button)
        cols = [Text(m) for m in l]
        cols += [AttrMap(self.select_button, 'text',
                         'button_secondary focus')]

        self.columns = Columns(cols)
        return self.columns
예제 #19
0
    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]
예제 #20
0
class ApplicationListView(WidgetWrap):
    def __init__(self, applications, metadata_controller, controller):
        self.controller = controller
        self.applications = applications
        assert(len(applications) > 0)
        self.metadata_controller = metadata_controller
        self.n_remaining = len(self.applications)
        self.widgets = self.build_widgets()
        super().__init__(self.widgets)
        self.pile.focus_position = 2
        self.selected_app_w = None
        self.handle_focus_changed()
        self.update_skip_rest_button()

    def selectable(self):
        return True

    def keypress(self, size, key):
        # handle keypress first, then get new focus widget
        rv = super().keypress(size, key)
        self.handle_focus_changed()
        return rv

    def handle_focus_changed(self):
        "Check if focused widget changed, then update readme."
        fw = self.pile.focus
        if not isinstance(fw, ApplicationWidget):
            return
        if fw != self.selected_app_w:
            self.selected_app_w = fw
            if fw is None:
                self.description_w.set_text("No selected application")
            else:
                self.metadata_controller.get_readme(
                    fw.application.csid.as_seriesname(),
                    partial(self._handle_readme_load, fw))

    def _handle_readme_load(self, fw, readme_f):
        if self.selected_app_w == fw:
            EventLoop.loop.event_loop._loop.call_soon_threadsafe(
                partial(self._update_readme_on_main_thread,
                        readme_f.result()))

    def _update_readme_on_main_thread(self, readme):
        rt = self._trim_readme(readme)
        self.description_w.set_text(rt)

    def _trim_readme(self, readme):
        rls = readme.splitlines()
        rls = [l for l in rls if not l.startswith("#")]
        nrls = []
        for i in range(len(rls)):
            if i+1 == len(rls):
                break
            if len(rls[i]) > 0:
                if rls[i][0] in ['-', '#', '=']:
                    continue
            if len(rls[i+1]) > 0:
                if rls[i+1][0] in ['-', '=']:
                    continue
            nrls.append(rls[i])

        if len(nrls) == 0:
            return

        if nrls[0] == '':
            nrls = nrls[1:]
        # split after two paragraphs:
        if '' in nrls:
            firstparidx = nrls.index('')
        else:
            firstparidx = 1
        try:
            splitidx = nrls.index('', firstparidx + 1)
        except:
            splitidx = firstparidx
        nrls = nrls[:splitidx]
        return "\n".join(nrls)

    def build_widgets(self):
        ws = [Text("{} Applications in {}:".format(len(self.applications),
                                                   app.config['spell']))]
        max_app_name_len = max([len(a.service_name) for a in
                                self.applications])

        for a in self.applications:
            ws.append(Text(""))
            ws.append(ApplicationWidget(a, max_app_name_len,
                                        self.controller,
                                        self.do_deploy))

        self.description_w = Text("App description")
        ws += [HR(), self.description_w]

        self.skip_rest_button = PlainButton(
            "Deploy all",
            self.controller.do_deploy_remaining
        )
        cws = [('weight', 1, Text(" ")),
               (20, Color.button_secondary(
                   self.skip_rest_button,
                   focus_map='button_secondary focus'))]
        self.button_columns = Columns(cws, dividechars=1)

        ws += [HR(), self.button_columns]
        self.pile = Pile(ws)
        return Padding.center_90(Filler(self.pile, valign="top"))

    def do_deploy(self, application, sender):
        self.n_remaining -= 1
        self.update_skip_rest_button()
        self.selected_app_w.remove_buttons()

        self.controller.do_deploy(application,
                                  msg_cb=self.selected_app_w.set_progress)
        if self.n_remaining > 0:
            # find next available app widget to highlight. Start after
            # the current one and wrap around to top
            next_w = None
            reordered = self.pile.contents[self.pile.focus_position:] + \
                self.pile.contents[:self.pile.focus_position]
            for w, opts in reordered:
                if isinstance(w, ApplicationWidget) and w.selectable():
                    next_w = w
                    break
            ni = [w for w, _ in self.pile.contents].index(next_w)
            self.pile.focus_position = ni
        else:
            self.controller.finish()

    def update_skip_rest_button(self):
        t = "Deploy all {} Remaining Applications".format(
            self.n_remaining)
        self.skip_rest_button.set_label(t)
예제 #21
0
class MachineWidget(WidgetWrap):
    """A widget displaying a machine and one action button.

    machine - the machine to display

    select_cb - a function that takes a machine to select

    unselect_cb - a function that takes a machine to un-select

    target_info - a string describing what we're pinning to

    current_pin_cb - a function that takes a machine and returns a
    string if it's already pinned or None if it is not

    """
    def __init__(self, machine, select_cb, unselect_cb, target_info,
                 current_pin_cb):
        self.machine = machine
        self.select_cb = select_cb
        self.unselect_cb = unselect_cb
        self.target_info = target_info
        self.current_pin_cb = current_pin_cb

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

    def selectable(self):
        return True

    def build_widgets(self):
        m = self.machine
        l = [
            '{:20s}'.format(m.hostname), '{:3d}'.format(m.cpu_cores),
            '{:6s}'.format(m.mem), '{:10s}'.format(m.storage)
        ]

        self.select_button_label = "Pin {} to {}".format(
            m.hostname, self.target_info)
        self.unselect_button_label = "Un-pin {} from ".format(
            m.hostname) + "{}"

        self.select_button = PlainButton(self.select_button_label,
                                         self.handle_button)
        cols = [Text(m) for m in l]
        cols += [AttrMap(self.select_button, 'text', 'button_secondary focus')]

        self.columns = Columns(cols)
        return self.columns

    def update_machine(self):
        """Refresh with potentially updated machine info from controller.
        Assumes that machine exists - machines going away is handled
        in machineslist.update().
        """
        machines = app.maas.client.get_machines()
        if machines is None:
            return
        self.machine = next(
            (m for m in machines if m.instance_id == self.machine.instance_id),
            None)

    def __repr__(self):
        return "widget for " + str(self.machine)

    def update(self):
        self.update_machine()
        current_pin = self.current_pin_cb(self.machine)
        if current_pin:
            l = self.unselect_button_label.format(current_pin)
            self.select_button.set_label(l)
        else:
            self.select_button.set_label(self.select_button_label)

    def handle_button(self, sender):
        if self.current_pin_cb(self.machine):
            self.unselect_cb(self.machine)
        else:
            self.select_cb(self.machine)

        self.update()
예제 #22
0
class ApplicationConfigureView(WidgetWrap):

    def __init__(self, application, metadata_controller, controller):
        self.controller = controller
        self.application = application
        self.options_copy = self.application.options.copy()
        self.metadata_controller = metadata_controller
        self.widgets = self.build_widgets()
        self.description_w = Text("")
        self.showing_all = False
        self.buttons_selected = False
        self.frame = Frame(body=self.build_widgets(),
                           footer=self.build_footer())
        super().__init__(self.frame)

        self.metadata_controller.get_readme(
            self.application.csid.as_seriesname(),
            partial(self._handle_readme_load))

    def _handle_readme_load(self, readme_f):
        EventLoop.loop.event_loop._loop.call_soon_threadsafe(
            partial(self._update_readme_on_main_thread,
                    readme_f.result()))

    def _update_readme_on_main_thread(self, readme):
        rt = self._trim_readme(readme)
        self.description_w.set_text(rt)

    def _trim_readme(self, readme):
        rls = readme.splitlines()
        rls = [l for l in rls if not l.startswith("#")]
        nrls = []
        for i in range(len(rls)):
            if i + 1 == len(rls):
                break
            if len(rls[i]) > 0:
                if rls[i][0] in ['-', '#', '=']:
                    continue
            if len(rls[i + 1]) > 0:
                if rls[i + 1][0] in ['-', '=']:
                    continue
            nrls.append(rls[i])

        if len(nrls) == 0:
            return

        if nrls[0] == '':
            nrls = nrls[1:]
        # split after two paragraphs:
        if '' in nrls:
            firstparidx = nrls.index('')
        else:
            firstparidx = 1
        try:
            splitidx = nrls.index('', firstparidx + 1)
        except:
            splitidx = firstparidx
        nrls = nrls[:splitidx]
        return "\n".join(nrls)

    def selectable(self):
        return True

    def keypress(self, size, key):
        # handle keypress first, then get new focus widget
        rv = super().keypress(size, key)
        if key in ['tab', 'shift tab']:
            self._swap_focus()
        return rv

    def _swap_focus(self):
        if not self.buttons_selected:
            self.buttons_selected = True
            self.frame.focus_position = 'footer'
            self.buttons.focus_position = 3
        else:
            self.buttons_selected = False
            self.frame.focus_position = 'body'

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

    def build_footer(self):
        cancel = menu_btn(on_press=self.do_cancel,
                          label="\n  BACK\n")
        confirm = menu_btn(on_press=self.do_commit,
                           label="\n APPLY CHANGES\n")
        self.buttons = Columns([
            ('fixed', 2, Text("")),
            ('fixed', 13, Color.menu_button(
                cancel,
                focus_map='button_primary focus')),
            Text(""),
            ('fixed', 20, Color.menu_button(
                confirm,
                focus_map='button_primary focus')),
            ('fixed', 2, Text(""))
        ])

        footer = Pile([
            HR(top=0),
            Padding.center_90(self.description_w),
            Padding.line_break(""),
            Color.frame_footer(Pile([
                Padding.line_break(""),
                self.buttons]))
        ])

        return footer

    def get_whitelisted_option_widgets(self):
        service_id = self.application.csid.as_str_without_rev()
        options = self.metadata_controller.get_options(service_id)

        svc_opts_whitelist = utils.get_options_whitelist(
            self.application.service_name)
        hidden = [n for n in options.keys() if n not in svc_opts_whitelist]
        log.info("Hiding options not in the whitelist: {}".format(hidden))

        return self._get_option_widgets(svc_opts_whitelist, options)

    def get_non_whitelisted_option_widgets(self):
        service_id = self.application.csid.as_str_without_rev()
        options = self.metadata_controller.get_options(service_id)

        svc_opts_whitelist = utils.get_options_whitelist(
            self.application.service_name)
        hidden = [n for n in options.keys() if n not in svc_opts_whitelist]
        return self._get_option_widgets(hidden, options)

    def _get_option_widgets(self, opnames, options):
        ws = []
        for opname in opnames:
            opdict = options[opname]
            cv = self.application.options.get(opname, None)
            ow = OptionWidget(opname,
                              opdict['Type'],
                              opdict['Description'],
                              opdict['Default'],
                              current_value=cv,
                              value_changed_callback=self.handle_edit)
            ws.append(ow)
        return ws

    def do_toggle_show_all_config(self, sender):
        if not self.showing_all:
            new_ows = self.get_non_whitelisted_option_widgets()
            header = Text("Advanced Configuration Options")
            opts = self.pile.options()
            self.pile.contents.append((header, opts))
            for ow in new_ows:
                self.pile.contents.append((ow, opts))
            self.toggle_show_all_button.set_label(
                "Hide Advanced Configuration")
            self.showing_all = True
        else:
            i = self.toggle_show_all_button_index
            self.pile.contents = self.pile.contents[:i + 1]
            self.toggle_show_all_button.set_label(
                "Show Advanced Configuration")
            self.showing_all = False

    def handle_edit(self, opname, value):
        self.options_copy[opname] = value

    def handle_scale(self, opname, scale):
        self.application.num_units = scale

    def do_cancel(self, sender):
        self.controller.handle_sub_view_done()

    def do_commit(self, sender):
        self.application.options = self.options_copy
        self.controller.handle_sub_view_done()
예제 #23
0
class MachineWidget(WidgetWrap):

    """A widget displaying a machine and one action button.

    machine - the machine to display

    select_cb - a function that takes a machine to select

    unselect_cb - a function that takes a machine to un-select

    target_info - a string describing what we're pinning to

    current_pin_cb - a function that takes a machine and returns a
    string if it's already pinned or None if it is not

    """

    def __init__(self, machine, select_cb, unselect_cb,
                 target_info, current_pin_cb):
        self.machine = machine
        self.select_cb = select_cb
        self.unselect_cb = unselect_cb
        self.target_info = target_info
        self.current_pin_cb = current_pin_cb

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

    def selectable(self):
        return True

    def build_widgets(self):
        m = self.machine
        l = ['{:20s}'.format(m.hostname),
             '{:3d}'.format(m.cpu_cores),
             '{:6s}'.format(m.mem),
             '{:10s}'.format(m.storage)]

        self.select_button_label = "Pin {} to {}".format(m.hostname,
                                                         self.target_info)
        self.unselect_button_label = "Un-pin {} from ".format(
            m.hostname) + "{}"

        self.select_button = PlainButton(self.select_button_label,
                                         self.handle_button)
        cols = [Text(m) for m in l]
        cols += [AttrMap(self.select_button, 'text',
                         'button_secondary focus')]

        self.columns = Columns(cols)
        return self.columns

    def update_machine(self):
        """Refresh with potentially updated machine info from controller.
        Assumes that machine exists - machines going away is handled
        in machineslist.update().
        """
        machines = app.maas.client.get_machines()
        if machines is None:
            return
        self.machine = next((m for m in machines
                             if m.instance_id == self.machine.instance_id),
                            None)

    def __repr__(self):
        return "widget for " + str(self.machine)

    def update(self):
        self.update_machine()
        current_pin = self.current_pin_cb(self.machine)
        if current_pin:
            l = self.unselect_button_label.format(current_pin)
            self.select_button.set_label(l)
        else:
            self.select_button.set_label(self.select_button_label)

    def handle_button(self, sender):
        if self.current_pin_cb(self.machine):
            self.unselect_cb(self.machine)
        else:
            self.select_cb(self.machine)

        self.update()
예제 #24
0
class ApplicationConfigureView(WidgetWrap):
    def __init__(self, application, metadata_controller, controller):
        self.controller = controller
        self.application = application
        self.options_copy = self.application.options.copy()
        self.num_units_copy = self.application.num_units
        self.metadata_controller = metadata_controller
        self.widgets = self.build_widgets()
        self.description_w = Text("")
        self.showing_all = False
        self.buttons_selected = False
        self.frame = Frame(body=self.build_widgets(),
                           footer=self.build_footer())
        super().__init__(self.frame)

        self.metadata_controller.get_readme(
            self.application.csid.as_seriesname(),
            partial(self._handle_readme_load))

    def _handle_readme_load(self, readme_f):
        EventLoop.loop.event_loop._loop.call_soon_threadsafe(
            partial(self._update_readme_on_main_thread, readme_f.result()))

    def _update_readme_on_main_thread(self, readme):
        rt = self._trim_readme(readme)
        self.description_w.set_text(rt)

    def _trim_readme(self, readme):
        rls = readme.splitlines()
        rls = [l for l in rls if not l.startswith("#")]
        nrls = []
        for i in range(len(rls)):
            if i + 1 == len(rls):
                break
            if len(rls[i]) > 0:
                if rls[i][0] in ['-', '#', '=']:
                    continue
            if len(rls[i + 1]) > 0:
                if rls[i + 1][0] in ['-', '=']:
                    continue
            nrls.append(rls[i])

        if len(nrls) == 0:
            return

        if nrls[0] == '':
            nrls = nrls[1:]
        # split after two paragraphs:
        if '' in nrls:
            firstparidx = nrls.index('')
        else:
            firstparidx = 1
        try:
            splitidx = nrls.index('', firstparidx + 1)
        except:
            splitidx = firstparidx
        nrls = nrls[:splitidx]
        return "\n".join(nrls)

    def selectable(self):
        return True

    def keypress(self, size, key):
        # handle keypress first, then get new focus widget
        rv = super().keypress(size, key)
        if key in ['tab', 'shift tab']:
            self._swap_focus()
        return rv

    def _swap_focus(self):
        if not self.buttons_selected:
            self.buttons_selected = True
            self.frame.focus_position = 'footer'
            self.buttons.focus_position = 3
        else:
            self.buttons_selected = False
            self.frame.focus_position = 'body'

    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.num_units_copy,
                                   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"))

    def build_footer(self):
        cancel = menu_btn(on_press=self.do_cancel, label="\n  BACK\n")
        confirm = menu_btn(on_press=self.do_commit, label="\n APPLY CHANGES\n")
        self.buttons = Columns([
            ('fixed', 2, Text("")),
            ('fixed', 13,
             Color.menu_button(cancel, focus_map='button_primary focus')),
            Text(""),
            ('fixed', 20,
             Color.menu_button(confirm, focus_map='button_primary focus')),
            ('fixed', 2, Text(""))
        ])

        footer = Pile([
            HR(top=0),
            Padding.center_90(self.description_w),
            Padding.line_break(""),
            Color.frame_footer(Pile([Padding.line_break(""), self.buttons]))
        ])

        return footer

    def get_whitelisted_option_widgets(self):
        service_id = self.application.csid.as_str_without_rev()
        options = self.metadata_controller.get_options(service_id)

        svc_opts_whitelist = utils.get_options_whitelist(
            self.application.service_name)
        hidden = [n for n in options.keys() if n not in svc_opts_whitelist]
        log.info("Hiding options not in the whitelist: {}".format(hidden))

        return self._get_option_widgets(svc_opts_whitelist, options)

    def get_non_whitelisted_option_widgets(self):
        service_id = self.application.csid.as_str_without_rev()
        options = self.metadata_controller.get_options(service_id)

        svc_opts_whitelist = utils.get_options_whitelist(
            self.application.service_name)
        hidden = [n for n in options.keys() if n not in svc_opts_whitelist]
        return self._get_option_widgets(hidden, options)

    def _get_option_widgets(self, opnames, options):
        ws = []
        for opname in opnames:
            try:
                opdict = options[opname]
            except KeyError:
                app.log.debug(
                    "Unknown charm option ({}), skipping".format(opname))
                continue
            cv = self.application.options.get(opname, None)
            ow = OptionWidget(opname,
                              opdict['Type'],
                              opdict['Description'],
                              opdict['Default'],
                              current_value=cv,
                              value_changed_callback=self.handle_edit)
            ws.append(ow)
        return ws

    def do_toggle_show_all_config(self, sender):
        if not self.showing_all:
            new_ows = self.get_non_whitelisted_option_widgets()
            header = Text("Advanced Configuration Options")
            opts = self.pile.options()
            self.pile.contents.append((header, opts))
            for ow in new_ows:
                self.pile.contents.append((ow, opts))
            self.toggle_show_all_button.set_label(
                "Hide Advanced Configuration")
            self.showing_all = True
        else:
            i = self.toggle_show_all_button_index
            self.pile.contents = self.pile.contents[:i + 1]
            self.toggle_show_all_button.set_label(
                "Show Advanced Configuration")
            self.showing_all = False

    def handle_edit(self, opname, value):
        self.options_copy[opname] = value

    def handle_scale(self, opname, scale):
        self.num_units_copy = scale

    def do_cancel(self, sender):
        self.controller.handle_sub_view_done()

    def do_commit(self, sender):
        self.application.options = self.options_copy
        self.application.num_units = self.num_units_copy
        self.controller.handle_sub_view_done()