Ejemplo n.º 1
0
    def build_widget(self):
        self.step_pile = pile = Pile([
            Columns([
                ('fixed', 3, self.icon),
                self.description,
            ],
                    dividechars=1),
            Padding.line_break(""),
            Padding.push_4(self.output),
        ])

        if utils.is_linux() and self.model.needs_sudo:
            pile.contents.append((Padding.line_break(""), pile.options()))
            label = 'This step requires sudo.'
            if not self.app.sudo_pass:
                label += '  Enter sudo password, if needed:'
                self.sudo_input = PasswordEditor()
            columns = [
                ('weight', 0.5, Padding.left(Text(('body', label)), left=5)),
            ]
            if self.sudo_input:
                columns.append(
                    ('weight', 1,
                     Color.string_input(self.sudo_input,
                                        focus_map='string_input focus')))
            pile.contents.append((Columns(columns,
                                          dividechars=3), pile.options()))
Ejemplo n.º 2
0
class ApplicationWidget(WidgetWrap):

    def __init__(self, application, maxlen, controller, deploy_cb,
                 hide_config=False):
        self.application = application
        self.controller = controller
        self.deploy_cb = deploy_cb
        self.hide_config = hide_config
        self._selectable = True
        super().__init__(self.build_widgets(maxlen))
        self.columns.focus_position = len(self.columns.contents) - 1

    def __repr__(self):
        return "<ApplicationWidget for {}>".format(
            self.application.service_name)

    def selectable(self):
        return self._selectable

    def update(self):
        self.unit_w.set_text("Units: {:4d}".format(self.application.num_units))

    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

    def remove_buttons(self):
        self._selectable = False
        self.columns.contents = self.columns.contents[:-2]
        self.columns.contents.append((Text(""),
                                      self.columns.options()))

    def set_progress(self, progress_str):
        self.columns.contents[-1] = (Text(progress_str, align='right'),
                                     self.columns.options())
Ejemplo n.º 3
0
 def build_widget(self):
     rows = [
         Text("Select primary/external network, "
              "and datastore for this deployment:"),
         HR(),
         Columns([
             ('weight', 0.5, Text('primary network', align="right")),
             Color.string_input(
                 self.vsphere_config['primary-network'],
                 focus_map='string_input focus')
         ], dividechars=1),
         HR(),
         Columns([
             ('weight', 0.5, Text('external network (optional)',
                                  align="right")),
             Color.string_input(
                 self.vsphere_config['external-network'],
                 focus_map='string_input focus')
         ], dividechars=1),
         HR(),
         Columns([
             ('weight', 0.5, Text('datastore', align="right")),
             Color.string_input(
                 self.vsphere_config['datastore'],
                 focus_map='string_input focus')
         ], dividechars=1)
     ]
     self.pile = Pile(rows)
     return self.pile
Ejemplo n.º 4
0
 def _build_model_inputs(self):
     items = [
         Columns(
             [
                 ("weight", 0.2, Text("Ceph MON", align="right")),
                 ("weight", 0.3,
                  Color.string_input(self.ceph_mon,
                                     focus_map="string_input focus"))
             ],
             dividechars=4
         ),
         Columns(
             [
                 ("weight", 0.2, Text("Username",
                                      align="right")),
                 ("weight", 0.3,
                  Color.string_input(self.username,
                                     focus_map="string_input focus"))
             ],
             dividechars=4
         ),
         Columns(
             [
                 ("weight", 0.2, Text("Key", align="right")),
                 ("weight", 0.3,
                  Color.string_input(self.ceph_key,
                                     focus_map="string_input focus"))
             ],
             dividechars=4
         )
     ]
     return Pile(items)
Ejemplo n.º 5
0
 def __init__(self, keyMap=None, bindArgs=None, bindKwargs=None):
     self._keyMap = keyMap or {}
     self._bindArgs = bindArgs or ()
     self._bindKwargs = bindKwargs or {}
     self._state = None
     self._w = Columns([], 1)
     self.refresh()
     super().__init__(self._w, 'style1')
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
 def __init__(self, text):
     self._editable = False
     self._content = Text(text)
     self._root = Columns(())
     self._content_options = self._root.options('pack')
     self._left = Text("("), self._root.options('given', 1)
     self._right = Text(")"), self._root.options('given', 1)
     self._update()
     super().__init__(self._root)
Ejemplo n.º 9
0
    def build_widgets(self):

        self.services_column = ServicesColumn(self.display_controller,
                                              self.placement_controller, self)

        self.machines_column = MachinesColumn(self.display_controller,
                                              self.placement_controller, self)
        self.relations_column = RelationsColumn(self.display_controller,
                                                self.placement_controller,
                                                self, self.metadata_controller)
        self.charmstore_column = CharmstoreColumn(self.display_controller,
                                                  self.placement_controller,
                                                  self,
                                                  self.metadata_controller)
        self.options_column = OptionsColumn(self.display_controller,
                                            self.placement_controller, self,
                                            self.metadata_controller)

        self.machines_header = self.get_machines_header(self.machines_column)
        self.relations_header = self.get_relations_header()
        self.services_header = self.get_services_header()
        self.charmstore_header = self.get_charmstore_header(
            self.charmstore_column)
        self.options_header = self.get_options_header(self.options_column)

        cs = [self.services_header, self.charmstore_header]

        self.header_columns = Columns(cs, dividechars=2)

        self.columns = Columns([self.services_column, self.machines_column],
                               dividechars=2)

        self.deploy_button = MenuSelectButton("\nCommit\n",
                                              on_press=self.do_deploy)
        self.deploy_button_label = Text("Some charms use default")
        self.placement_edit_body_pile = Pile([self.columns])
        self.placement_edit_body = Filler(Padding(
            self.placement_edit_body_pile,
            align='center',
            width=('relative', 95)),
                                          valign='top')
        self.bundle_graph_text = Text("No graph to display yet.")
        self.bundle_graph_widget = Padding(self.bundle_graph_text, 'center',
                                           'pack')
        b = AttrMap(self.deploy_button, 'frame_header', 'button_primary focus')
        self.footer_grid = GridFlow(
            [self.deploy_button_label,
             Padding(b, width=28, align='center')], 28, 1, 1, 'right')
        f = AttrMap(self.footer_grid, 'frame_footer', 'frame_footer')

        self.frame = Frame(header=Pile([self.header_columns,
                                        HR()]),
                           body=self.placement_edit_body,
                           footer=f)
        return self.frame
Ejemplo n.º 10
0
	def __init__(self, data, parent, color=None, **kwargs):
		self.table = parent

		self.color = color

		self.dict = data

		Columns.__init__(self, [], **kwargs)

		for header in self.table.columns:
			self.contents.append(self.build_cell(header))
Ejemplo n.º 11
0
 def _display_schedule(self, s, f, date, schedule):
     rows = []
     for fitness in self.fitness:
         for cols in self._single_response(s, f, date, schedule, fitness,
                                           schedule.frame_type == 'd'):
             rows.append(Columns(cols))
     for fatigue in self.fatigue:
         for cols in self._single_response(s, f, date, schedule, fatigue,
                                           schedule.frame_type == 'd'):
             rows.append(Columns(cols))
     if rows:
         yield Pile([Text('SHRIMP'), Indent(Pile(rows))])
Ejemplo n.º 12
0
 def build_widget(self):
     return [
         Columns([('fixed', 16, Text('network bridge', align="right")),
                  Color.string_input(self.lxd_config['network'],
                                     focus_map='string_input focus')],
                 dividechars=1),
         HR(),
         Columns([('fixed', 16, Text('storage pool', align="right")),
                  Color.string_input(self.lxd_config['storage-pool'],
                                     focus_map='string_input focus')],
                 dividechars=1),
     ]
Ejemplo n.º 13
0
    def __init__(self,
                 height,
                 directory=".",
                 file="",
                 attr=(None, None),
                 show_hidden=False):
        """
        height -- height of the directory list and the file list
        directory, file -- default selection
        attr -- (inner selectable widgets, selected widgets)
        show_hidden -- If True, hidden files are shown by default.
        """

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

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

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

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

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

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

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

        self.update_widgets()

        WidgetWrap.__init__(self, self.outer_widget)
Ejemplo n.º 14
0
class ApplicationWidget(WidgetWrap):

    def __init__(self, application, maxlen, controller, deploy_cb):
        self.application = application
        self.controller = controller
        self.deploy_cb = deploy_cb
        self._selectable = True
        super().__init__(self.build_widgets(maxlen))
        self.columns.focus_position = len(self.columns.contents) - 1

    def __repr__(self):
        return "<ApplicationWidget for {}>".format(
            self.application.service_name)

    def selectable(self):
        return self._selectable

    def build_widgets(self, maxlen):
        num_str = "{}".format(self.application.num_units)
        col_pad = 6

        cws = [
                (maxlen + col_pad,
                 Text(self.application.service_name)),
                (7 + len(num_str), Text('Units: {}'.format(num_str),
                                        align='right')),
                # placeholder for instance type
                ('weight', 1, Text(" ")),
                (20, Color.button_secondary(
                    PlainButton("Configure",
                                partial(self.controller.do_configure,
                                        self.application)),
                    focus_map='button_secondary focus')),
                (20, Color.button_primary(
                    PlainButton("Deploy",
                                partial(self.deploy_cb,
                                        self.application)),
                    focus_map='button_primary focus'))
            ]
        self.columns = Columns(cws, dividechars=1)
        return self.columns

    def remove_buttons(self):
        self._selectable = False
        self.columns.contents = self.columns.contents[:-2]
        self.columns.contents.append((Text(""),
                                     self.columns.options()))

    def set_progress(self, progress_str):
        self.columns.contents[-1] = (Text(progress_str, align='right'),
                                     self.columns.options())
Ejemplo n.º 15
0
 def _container(self):
     total_items = [
         Columns([("weight", 0.2, Text("Format", align="right")),
                  ("weight", 0.3,
                   Color.string_input(self._format_edit(),
                                      focus_map="string_input focus"))],
                 dividechars=4),
         Columns([("weight", 0.2, Text("Mount", align="right")),
                  ("weight", 0.3,
                   Color.string_input(self.mountpoint,
                                      focus_map="string_input focs"))],
                 dividechars=4)
     ]
     return Pile(total_items)
Ejemplo n.º 16
0
class HotkeyBar(
        AttrWrap, ):
    def __init__(self, keyMap=None, bindArgs=None, bindKwargs=None):
        self._keyMap = keyMap or {}
        self._bindArgs = bindArgs or ()
        self._bindKwargs = bindKwargs or {}
        self._state = None
        self._w = Columns([], 1)
        self.refresh()
        super().__init__(self._w, 'style1')

    def refresh(self):
        tMap = self._keyMap
        res = []
        if self._state is not None:
            n, tMap = self._state
            w = AttrWrap(Text(f'{n}:'), 'style1bold')
            res.append((w, self._w.options('pack')))
            res.append(
                (HotkeyItem('esc', '',
                            on_mouseLeft=self._fire), self._w.options('pack')))
        for key, o in tMap.items():
            res.append(
                (HotkeyItem(key, o[0],
                            on_mouseLeft=self._fire), self._w.options('pack')))
        self._w.contents = res

    def _fire(self, key=None):
        if not key: return
        tMap = self._keyMap if self._state is None else self._state[1]
        if key == 'esc' and self._state is not None:
            self._state = None
            self.refresh()
        elif key in tMap:
            n, v = tMap[key]
            print('HOTKEY', key, n, v)
            if callable(v):
                _args = (n, ) + self._bindArgs
                _kwargs = self._bindKwargs
                v(*_args, **_kwargs)
                self._state = None
            else:
                self._state = (n, v)
            self.refresh()
        else:
            return False
        return True

    def keypress(self, size, key):
        if not self._fire(key): return key
Ejemplo n.º 17
0
 def _journal_date(self, s, f, ajournal, date):
     zones = build_zones(s, ajournal, HRZ_WIDTH)
     active_date = self.__active_date(s, ajournal, date)
     climbs = self.__climbs(s, ajournal, date)
     details = Pile(([] if climbs else [Divider()]) + active_date + climbs)
     yield Pile([
         Text(ajournal.name),
         Indent(Columns([details, (HRZ_WIDTH + 2, zones)])),
         Divider(),
         Indent(Columns([Pile(self.__template(s, ajournal, MIN_KM_TIME_ANY, 'Min Time', r'(\d+km)', date) +
                              self.__template(s, ajournal, MED_KM_TIME_ANY, 'Med Time', r'(\d+km)', date)),
                         Pile(self.__template(s, ajournal, MAX_MED_HR_M_ANY, 'Max Med Heart Rate', r'(\d+m)', date) +
                              self.__template(s, ajournal, MAX_MEAN_PE_M_ANY, 'Max Mean Power Estimate', r'(\d+m)', date))]))
     ])
Ejemplo n.º 18
0
 def build_results(self):
     rows = []
     rows.append(
         Columns([('weight', 0.1, Text('Application')),
                  ('weight', 0.4, Text('Result'))],
                 dividechars=5))
     rows.append(HR())
     rows.append(Padding.line_break(""))
     for k, v in self.results.items():
         rows.append(
             Columns([('weight', 0.1, Text(k)), ('weight', 0.4, Text(v))],
                     dividechars=5))
         self.app.log.debug(rows)
     return rows
Ejemplo n.º 19
0
    def __init__(self, original_widget, title="",
                 tlcorner=u'┌', tline=u'─', lline=u'│',
                 trcorner=u'┐', blcorner=u'└', rline=u'│',
                 bline=u'─', brcorner=u'┘'):
        """
        Use 'title' to set an initial title text with will be centered
        on top of the box.

        You can also override the widgets used for the lines/corners:
            tline: top line
            bline: bottom line
            lline: left line
            rline: right line
            tlcorner: top left corner
            trcorner: top right corner
            blcorner: bottom left corner
            brcorner: bottom right corner
        """

        tline, bline = Divider(tline), Divider(bline)
        lline, rline = SolidFill(lline), SolidFill(rline)
        tlcorner, trcorner = Text(tlcorner), Text(trcorner)
        blcorner, brcorner = Text(blcorner), Text(brcorner)

        title_widget = ('fixed', len(title), AttrMap(Text(title), 'header'))
        top = Columns([
            ('fixed', 1, tlcorner),
            title_widget,
            tline,
            ('fixed', 1, trcorner)
        ])

        middle = Columns([('fixed', 1, lline),
                          original_widget,
                          ('fixed', 1, rline)],
                         box_columns=[0, 2],
                         focus_column=1)

        bottom = Columns([('fixed', 1, blcorner),
                          bline,
                          ('fixed', 1, brcorner)])

        pile = Pile([('flow', top),
                    middle,
                    ('flow', bottom)],
                    focus_item=1)

        # super?
        WidgetDecoration.__init__(self, original_widget)
        WidgetWrap.__init__(self, pile)
Ejemplo n.º 20
0
    def _layout(self):
        self._wallpaper_count = Text(str(len(self._wpctrl.wallpapers)))
        self._info = Text("", wrap='clip')
        self._head = Columns([('pack', self._wallpaper_count),
                              (10, Text("Wallpapers")),
                              self._info],
                             dividechars=1)
        header = Pile([self._head, AttrMap(Divider("─"), 'divider')])

        self._screens = [ScreenWidget(screen, self._scrctrl)
                         for screen in self._scrctrl.screens]
        body = ListBoxWithTabSupport(self._screens)

        self._root = Frame(header=header, body=body)
Ejemplo n.º 21
0
 def _w_init(self):
     self._w_prep()
     self.refresh()
     self._w = Columns([
         (1, self._w_indicator),
         Padding(Columns([
             (11,
              Padding(Pile([self._w_timestamp, self._w_statusbar]),
                      right=1)),
             (20, Padding(self._w_members, right=2)),
             Pile([self._w_subject, self._w_lastmsg]),
         ], 0),
                 left=1),
     ], 0)
Ejemplo n.º 22
0
Archivo: top.py Proyecto: mfkiwl/pstack
def top(period=1, get_state=get_demo_state):
    """Display process information.

    Arguments:
      - period (float)       : update period
      - get_state (callable) : function to generate state information
    """

    engine_table = Columns([])
    process_table = Columns([])

    def make_text(title, attr="", align="left"):
        """Create a Text object with given content, style and alignment."""
        return Text((attr, " %s" % title), align=align)

    def make_separator():
        """Create a separator."""
        return make_text("")

    def update():
        engines, processes = get_state()
        engine_table.contents = get_engine_table_content(engines)
        process_table.contents = get_process_table_content([header] +
                                                           processes)

    items = [
        make_separator(),
        make_text("Process Viewer", attr="section", align="center"),
        make_text("Engine Usage:"),
        LineBox(engine_table, **{arg: " "
                                 for arg in linebox_args}),
        make_text("Process Table:"),
        make_separator(),
        LineBox(process_table),
        make_separator(),
        make_text("Press (q) to quit.")
    ]

    def on_timer(loop, user_data):
        update()
        loop.set_alarm_in(period, on_timer)

    def on_key(key):
        if key in ('q', 'Q'):
            raise ExitMainLoop()

    loop = MainLoop(get_padded(items), palette, unhandled_input=on_key)
    on_timer(loop, None)  # Start timer
    loop.run()
Ejemplo n.º 23
0
def pack_widgets(branch, max_cols=N_COLUMNS):
    new_branch = [branch[0]]
    columns, width = [], 0
    for widget in branch[1:]:
        size = widget_size(widget)
        if size + width <= max_cols:
            columns.append((COLUMN_WIDTH * size, widget))
            width += size
        else:
            if columns:
                new_branch.append(Columns(columns))
            columns, width = [(COLUMN_WIDTH * size, widget)], size
    if columns:
        new_branch.append(Columns(columns))
    return default_after(new_branch)
Ejemplo n.º 24
0
    def __init__(self, num_rows=20, w=(14, 14, 18, 16, 16, 16, 20)):
        """
            @method __init__
            Initializes the widget
        """
        self.m_process_list = ProcessList(w)
        self.prev_sort_item = None

        self.w_status = HeaderButton('Status', 'status', self.handle_click)
        self.w_pid = HeaderButton('PID', 'pid', self.handle_click)
        self.w_name = HeaderButton('Name', 'name', self.handle_click)
        self.w_cpu = HeaderButton('CPU %', 'cpu_perc', self.handle_click)
        self.w_mem = HeaderButton('MEM %', 'mem_perc', self.handle_click)
        self.w_up = HeaderButton('Uptime', 'uptime', self.handle_click)
        self.w_pname = HeaderButton('Process', 'pname', self.handle_click)

        self.w_cpu.activate()
        self.prev_sort_item = self.w_cpu

        self.header_buttons = h = [
            self.w_status, self.w_pid, self.w_name, self.w_cpu, self.w_mem,
            self.w_up, self.w_pname
        ]

        m_header = AttrMap(
            Columns([('fixed', w[i], h[i]) for i in range(0, len(h))]),
            'invert')
        m_lb = ListBox(
            SimpleListWalker(
                [m_header, BoxAdapter(self.m_process_list, num_rows)]))
        super(ProcessTable, self).__init__(m_lb, None)
        self.update()
Ejemplo n.º 25
0
    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
Ejemplo n.º 26
0
    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
Ejemplo n.º 27
0
    def _build_node_waiting(self):
        """ creates a loading screen if nodes do not exist yet """
        text = [
            Text("\n\n\n"),
            Text(self.message, align="center"),
            Text("\n\n\n")
        ]

        load_box = [
            AttrWrap(Text("\u2582", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2581", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2583", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2584", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2585", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2586", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2587", align="center"), "pending_icon_on"),
            AttrWrap(Text("\u2588", align="center"), "pending_icon_on")
        ]

        # Add loading boxes
        random.shuffle(load_box)
        loading_boxes = []
        loading_boxes.append(('weight', 1, Text('')))
        for i in load_box:
            loading_boxes.append(
                ('pack', load_box[random.randrange(len(load_box))]))
        loading_boxes.append(('weight', 1, Text('')))
        loading_boxes = Columns(loading_boxes)

        return ScrollableListBox(text + [loading_boxes])
Ejemplo n.º 28
0
 def _make(self):
     down = QuickChangeBar(self._date, '<', -1, bar=self._bar)
     connect_signal(down.widget, 'change', self.date_change)
     up = QuickChangeBar(self._date, '>', 1, bar=self._bar)
     connect_signal(up.widget, 'change', self.date_change)
     year = YearBar(self._date, bar=self._bar)
     connect_signal(year.widget, 'change', self.date_change)
     month = MonthBar(self._date, as_text=False, bar=self._bar)
     connect_signal(month.widget, 'change', self.date_change)
     day_of_month = DayOfMonthBar(self._date, bar=self._bar)
     connect_signal(day_of_month.widget, 'change', self.date_change)
     day_of_week = DayOfWeekBar(self._date, bar=self._bar)
     connect_signal(day_of_week.widget, 'change', self.date_change)
     return Columns([
         (1, FocusAttr(down)),
         (1, Text(" ")),
         (4, Padding(FocusAttr(year), align='center', width='pack')),
         (1, Text("-")),
         (2, Padding(FocusAttr(month), align='center', width='pack')),
         (1, Text("-")),
         (2, Padding(FocusAttr(day_of_month), align='center',
                     width='pack')),
         (1, Text(" ")),
         (3, Padding(FocusAttr(day_of_week), align='center', width='pack')),
         (1, Text(" ")),
         (1, FocusAttr(up)),
     ])
Ejemplo n.º 29
0
    def build_footer(self):
        cancel = menu_btn(on_press=self.do_cancel,
                          label="\n  BACK\n")
        self.apply_button = menu_btn(on_press=self.do_commit,
                                     label="\n APPLY\n")
        self.buttons = Columns([
            ('fixed', 2, Text("")),
            ('fixed', 13, Color.menu_button(
                cancel,
                focus_map='button_primary focus')),
            Text(""),
            ('fixed', 20, Color.menu_button(
                self.apply_button,
                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
Ejemplo n.º 30
0
    def _build_node_waiting(self):
        """ creates a loading screen if nodes do not exist yet """
        text = [
            Padding.line_break(""),
            Text(self.message, align="center"),
            Padding.line_break("")
        ]

        load_box = [
            Color.pending_icon_on(Text("\u2581", align="center")),
            Color.pending_icon_on(Text("\u2582", align="center")),
            Color.pending_icon_on(Text("\u2583", align="center")),
            Color.pending_icon_on(Text("\u2584", align="center")),
            Color.pending_icon_on(Text("\u2585", align="center")),
            Color.pending_icon_on(Text("\u2586", align="center")),
            Color.pending_icon_on(Text("\u2587", align="center")),
            Color.pending_icon_on(Text("\u2588", align="center"))
        ]

        # Add loading boxes
        random.shuffle(load_box)
        loading_boxes = []
        loading_boxes.append(('weight', 1, Text('')))
        for i in load_box:
            loading_boxes.append(
                ('pack', load_box[random.randrange(len(load_box))]))
        loading_boxes.append(('weight', 1, Text('')))
        loading_boxes = Columns(loading_boxes)

        return Filler(Pile(text + [loading_boxes]), valign="middle")
Ejemplo n.º 31
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
Ejemplo n.º 32
0
    def _construct_arrow_tip(self, pos):

        cols = []
        overall_width = self._icon_offset

        if self._icon_offset > 0:
            # how often do we repeat the hbar_char until width icon_offset is reached
            hbar_char_count = len(self._arrow_hbar_char) / self._icon_offset
            barw = Text(self._arrow_hbar_char * hbar_char_count)
            bar = AttrMap(barw, self._arrow_hbar_att or self._arrow_att)
            cols.insert(1, (self._icon_offset, bar))

        # add icon only for non-leafs
        if self._walker.first_child_position(pos) is not None:
            iwidth, icon = self._construct_collapse_icon(pos)
            if icon is not None:
                cols.insert(0, (iwidth, icon))
                overall_width += iwidth

        # get arrow tip
        awidth, tip = ArrowTreeListWalker._construct_arrow_tip(self, pos)
        if tip is not None:
            cols.append((awidth, tip))
            overall_width += awidth

        return overall_width, Columns(cols)
Ejemplo n.º 33
0
 def _display_date(self, s, f, date):
     tomorrow = local_date_to_time(date + dt.timedelta(days=1))
     today = local_date_to_time(date)
     pile = []
     # todo - rewrite to use above
     for agroup in s.query(ActivityGroup).order_by(
             ActivityGroup.sort).all():
         segment_pile = []
         for sjournal in s.query(SegmentJournal). \
                 join(Segment). \
                 filter(SegmentJournal.start >= today,
                        SegmentJournal.start < tomorrow,
                        Segment.activity_group == agroup). \
                 order_by(SegmentJournal.start).all():
             columns = self.__fields(s, date, sjournal)
             if columns:
                 segment_pile.append(Columns(columns))
         if segment_pile:
             pile.append(
                 Pile([
                     Text(sjournal.segment.name),
                     Indent(Pile(segment_pile))
                 ]))
     if pile:
         yield Pile([Text('Segments'), Indent(Pile(pile))])
    def build_widget(self, **kwargs):

        def remove_p(charm_class):
            n = self.pc.assignment_machine_count_for_charm(charm_class)
            return n > 0

        def not_conflicted_p(cc):
            state, _, _ = self.pc.get_charm_state(cc)
            return state != CharmState.CONFLICTED

        actions = [(remove_p, 'Remove', self.do_remove),
                   (not_conflicted_p, 'Add', self.do_add)]
        self.unrequired_undeployed_sl = ServicesList(self.pc,
                                                     actions, actions,
                                                     ignore_deployed=True,
                                                     title="Un-Deployed")
        self.deployed_sl = ServicesList(self.pc,
                                        actions, actions,
                                        deployed_only=True,
                                        show_placements=True,
                                        title="Deployed Services")

        self.assigned_sl = ServicesList(self.pc,
                                        actions, actions,
                                        assigned_only=True,
                                        show_placements=True,
                                        title="Services to be Deployed")

        self.buttons = []
        self.button_grid = GridFlow(self.buttons, 22, 1, 1, 'center')
        self.pile1 = Pile([self.button_grid, self.assigned_sl,
                           self.unrequired_undeployed_sl])
        self.pile2 = Pile([self.deployed_sl])
        return LineBox(Columns([self.pile1, self.pile2]),
                       title="Add Services")
Ejemplo n.º 35
0
 def _build_widget(self, **kwargs):
     total_items = [
         Padding.center_60(Text(self.title, align="center")),
         Padding.center_60(
             Divider("\N{BOX DRAWINGS LIGHT HORIZONTAL}", 1, 1))
     ]
     if self.input_items:
         for item in self.input_items:
             key = item[0]
             caption = item[1]
             try:
                 mask = item[2]
             except:
                 mask = None
             self.input_selection[key] = StringEditor(caption="", mask=mask)
             col = Columns([
                 ("weight", 0.4, Text(caption, align="right")),
                 Color.string_input(self.input_selection[key],
                                    focus_map="string_input focus")
             ])
             total_items.append(Padding.center_60(col))
     total_items.append(
         Padding.center_60(
             Divider("\N{BOX DRAWINGS LIGHT HORIZONTAL}", 1, 1)))
     total_items.append(Padding.center_20(self._build_buttons()))
     return Filler(Pile(total_items), valign='middle')
Ejemplo n.º 36
0
    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
Ejemplo n.º 37
0
    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
Ejemplo n.º 38
0
Archivo: ui.py Proyecto: gigigi/turses
class TimelinesBuffer(WidgetWrap):
    """A widget that displays one or more `Timeline` objects."""

    def __init__(self, timelines=None, **kwargs):
        if timelines:
            timeline_widgets = [TimelineWidget(timeline, **kwargs) for timeline in timelines]
        else:
            timeline_widgets = []
        WidgetWrap.__init__(self, Columns(timeline_widgets))

    def scroll_up(self):
        active_widget = self._w.get_focus()
        active_widget.focus_previous()

    def scroll_down(self):
        active_widget = self._w.get_focus()
        active_widget.focus_next()

    def scroll_top(self):
        active_widget = self._w.get_focus()
        active_widget.focus_first()

    def scroll_bottom(self):
        active_widget = self._w.get_focus()
        active_widget.focus_last()

    def clear(self):
        """Clears the buffer."""
        # FIXME
        pass

    def render_timelines(self, timelines):
        """Renders the given statuses."""
        timeline_widgets = [TimelineWidget(timeline) for timeline in timelines]
        self._w = Columns(timeline_widgets) 

    def set_focus(self, index):
        active_widget = self._w.get_focus()
        active_widget.set_focus(index)

    def focus_timeline(self, index):
        self._w.set_focus_column(index)
Ejemplo n.º 39
0
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
Ejemplo n.º 40
0
Archivo: ui.py Proyecto: gigigi/turses
 def render_timelines(self, timelines):
     """Renders the given statuses."""
     timeline_widgets = [TimelineWidget(timeline) for timeline in timelines]
     self._w = Columns(timeline_widgets) 
Ejemplo n.º 41
0
class SimpleMachineWidget(WidgetWrap):

    """A widget displaying a machine. When selected, shows action buttons
    for placement types.

    machine - the machine to display

    controller - a PlacementController instance

    display_controller - a PlacerView instance

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

    """

    def __init__(self, machine, controller,
                 display_controller, show_assignments=True):
        self.machine = machine
        self.controller = controller
        self.display_controller = display_controller
        self.show_assignments = show_assignments
        self.is_selected = False
        w = self.build_widgets()
        super().__init__(w)
        self.update()

    def selectable(self):
        return True

    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 update_machine(self):
        """Refresh with potentially updated machine info from controller.
        Assumes that machine exists - machines going away is handled
        in machineslist.update().
        """
        self.machine = next((m for m in self.controller.machines()
                             if m.instance_id == self.machine.instance_id),
                            None)

    def update(self):
        self.update_machine()
        self.update_action_buttons()

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

    def info_markup(self):
        m = self.machine
        return [self.machine.hostname + "\n",
                'arch: {}  '.format(m.arch),
                'cores: {}  '.format(m.cpu_cores),
                'mem: {}  '.format(m.mem),
                'storage: {}'.format(m.storage)]

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

    def update_unselected(self):
        markup = self.info_markup()
        self.button.set_label(markup)
        self.pile.contents = [(AttrMap(self.button, 'text',
                                       'button_secondary focus'),
                               self.pile.options()),
                              (Divider(), self.pile.options())]

    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]

    def do_select(self, sender):
        if self.display_controller.selected_service is None:
            return
        self.is_selected = True
        self.update()
        self.pile.focus_position = 1
        self.action_button_cols.focus_position = 0

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

    def _do_select_assignment(self, atype):
        {AssignmentType.BareMetal:
         self.display_controller.do_select_baremetal,
         AssignmentType.LXD:
         self.display_controller.do_select_lxd,
         AssignmentType.KVM:
         self.display_controller.do_select_kvm}[atype](self.machine)
        self.pile.focus_position = 0

    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)
class PlacementView(WidgetWrap):

    """
    Handles display of machines and services.

    displays nothing if self.controller is not set.
    set it to a PlacementController.

    :param do_deploy_cb: deploy callback from controller
    """

    def __init__(self, display_controller, placement_controller,
                 config, do_deploy_cb,
                 initial_state=UIState.CHARMSTORE_VIEW,
                 has_maas=False):
        self.display_controller = display_controller
        self.placement_controller = placement_controller
        self.config = config
        self.do_deploy_cb = do_deploy_cb
        self.state = initial_state
        self.has_maas = has_maas
        self.prev_state = None
        self.showing_overlay = False
        self.showing_graph_split = False
        self.show_scc_graph = False
        self.bundle = placement_controller.bundle
        self.metadata_controller = MetadataController(self.bundle, config)
        w = self.build_widgets()
        super().__init__(w)
        self.reset_selections(top=True)  # calls self.update

    def scroll_down(self):
        pass

    def scroll_up(self):
        pass

    def focus_footer(self):
        self.frame.focus_position = 'footer'
        self.footer_grid.focus_position = 1

    def handle_tab(self, backward):
        tabloop = ['headercol1', 'col1', 'headercol2', 'col2', 'footer']

        if not self.has_maas:
            tabloop.remove('headercol1')

        def goto_header_col1():
            self.frame.focus_position = 'header'
            self.header_columns.focus_position = 0

        def goto_header_col2():
            self.frame.focus_position = 'header'
            self.header_columns.focus_position = 1

        def goto_col1():
            self.frame.focus_position = 'body'
            self.columns.focus_position = 0

        def goto_col2():
            self.frame.focus_position = 'body'
            if self.state == UIState.PLACEMENT_EDITOR:
                self.focus_machines_column()
            elif self.state == UIState.RELATION_EDITOR:
                self.focus_relations_column()
            elif self.state == UIState.OPTIONS_EDITOR:
                self.focus_options_column()
            else:
                self.focus_charmstore_column()

        actions = {'headercol1': goto_header_col1,
                   'headercol2': goto_header_col2,
                   'col1': goto_col1,
                   'col2': goto_col2,
                   'footer': self.focus_footer}

        if self.frame.focus_position == 'header':
            cur = ['headercol1',
                   'headercol2'][self.header_columns.focus_position]
        elif self.frame.focus_position == 'footer':
            cur = 'footer'
        else:
            cur = ['col1', 'col2'][self.columns.focus_position]

        cur_idx = tabloop.index(cur)

        if backward:
            next_idx = cur_idx - 1
        else:
            next_idx = (cur_idx + 1) % len(tabloop)

        actions[tabloop[next_idx]]()

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

        unhandled_key = self._w.keypress(size, key)
        if unhandled_key is None:
            return None
        elif unhandled_key in ['g', 'G']:
            if unhandled_key == 'G':
                self.show_scc_graph = True
            else:
                self.show_scc_graph = False
            self.showing_graph_split = not self.showing_graph_split
            if self.showing_graph_split:
                opts = self.placement_edit_body_pile.options()
                self.placement_edit_body_pile.contents.insert(
                    0, (self.bundle_graph_widget, opts))
            else:
                self.placement_edit_body_pile.contents.pop(0)
            self.update()
        else:
            return unhandled_key

    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)

    def get_charmstore_header(self, charmstore_column):
        series = self.placement_controller.bundle.series
        self.charm_search_widget = CharmStoreSearchWidget(self.do_add_charm,
                                                          charmstore_column,
                                                          self.config,
                                                          series)
        self.charm_search_header_pile = Pile([Divider(),
                                              Text(("body", "Add Charms"),
                                                   align='center'),
                                              Divider(),
                                              self.charm_search_widget])

        return self.charm_search_header_pile

    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

    def update_machines_header(self):
        maasinfo = self.placement_controller.maasinfo
        maasname = "'{}' <{}>".format(maasinfo['server_name'],
                                      maasinfo['server_hostname'])
        self.maastitle.set_text("Connected to MAAS {}".format(maasname))

    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]

    def get_relations_header(self):
        return Pile(self._simple_header_widgets("Relation Editor"))

    def get_options_header(self, options_column):
        simple_widgets = self._simple_header_widgets("Options Editor")
        fb = FilterBox(options_column.handle_filter_change,
                       info_text="Filter by option name")
        padded_fb = Padding(AttrMap(fb, 'filter', 'filter_focus'),
                            left=2, right=2)
        return Pile(simple_widgets + [padded_fb])

    def build_widgets(self):

        self.services_column = ServicesColumn(self.display_controller,
                                              self.placement_controller,
                                              self)

        self.machines_column = MachinesColumn(self.display_controller,
                                              self.placement_controller,
                                              self)
        self.relations_column = RelationsColumn(self.display_controller,
                                                self.placement_controller,
                                                self,
                                                self.metadata_controller)
        self.charmstore_column = CharmstoreColumn(self.display_controller,
                                                  self.placement_controller,
                                                  self,
                                                  self.metadata_controller)
        self.options_column = OptionsColumn(self.display_controller,
                                            self.placement_controller,
                                            self,
                                            self.metadata_controller)

        self.machines_header = self.get_machines_header(self.machines_column)
        self.relations_header = self.get_relations_header()
        self.services_header = self.get_services_header()
        self.charmstore_header = self.get_charmstore_header(
            self.charmstore_column)
        self.options_header = self.get_options_header(self.options_column)

        cs = [self.services_header, self.charmstore_header]

        self.header_columns = Columns(cs, dividechars=2)

        self.columns = Columns([self.services_column,
                                self.machines_column], dividechars=2)

        self.deploy_button = MenuSelectButton("\nCommit\n",
                                              on_press=self.do_deploy)
        self.deploy_button_label = Text("Some charms use default")
        self.placement_edit_body_pile = Pile([self.columns])
        self.placement_edit_body = Filler(Padding(
            self.placement_edit_body_pile,
            align='center',
            width=('relative', 95)),
            valign='top')
        self.bundle_graph_text = Text("No graph to display yet.")
        self.bundle_graph_widget = Padding(self.bundle_graph_text,
                                           'center', 'pack')
        b = AttrMap(self.deploy_button,
                    'frame_header',
                    'button_primary focus')
        self.footer_grid = GridFlow([self.deploy_button_label,
                                     Padding(b, width=28,
                                             align='center')],
                                    28, 1, 1, 'right')
        f = AttrMap(self.footer_grid,
                    'frame_footer',
                    'frame_footer')

        self.frame = Frame(header=Pile([self.header_columns, HR()]),
                           body=self.placement_edit_body,
                           footer=f)
        return self.frame

    def update(self):
        if self.prev_state != self.state:
            h_opts = self.header_columns.options()
            c_opts = self.columns.options()

            if self.state == UIState.PLACEMENT_EDITOR:
                self.update_machines_header()
                self.header_columns.contents[-1] = (self.machines_header,
                                                    h_opts)
                self.columns.contents[-1] = (self.machines_column, c_opts)

            elif self.state == UIState.RELATION_EDITOR:
                self.header_columns.contents[-1] = (self.relations_header,
                                                    h_opts)
                self.columns.contents[-1] = (self.relations_column, h_opts)
            elif self.state == UIState.CHARMSTORE_VIEW:
                self.header_columns.contents[-1] = (self.charmstore_header,
                                                    h_opts)
                self.columns.contents[-1] = (self.charmstore_column, h_opts)
            elif self.state == UIState.OPTIONS_EDITOR:
                self.header_columns.contents[-1] = (self.options_header,
                                                    h_opts)
                self.columns.contents[-1] = (self.options_column, h_opts)

            self.prev_state = self.state

        self.services_column.update()

        if self.state == UIState.PLACEMENT_EDITOR:
            self.machines_column.update()
        elif self.state == UIState.RELATION_EDITOR:
            self.relations_column.update()
        elif self.state == UIState.OPTIONS_EDITOR:
            self.options_column.update()
        else:
            self.charmstore_column.update()

        unplaced = self.placement_controller.unassigned_undeployed_services()
        all = self.placement_controller.services()
        n_subs_in_unplaced = len([c for c in unplaced if c.subordinate])
        n_subs_in_all = len([c for c in all if c.subordinate])

        n_total = len(all) - n_subs_in_all
        remaining = len(unplaced) - n_subs_in_unplaced
        if remaining > 0:
            dmsg = "\nAuto-assigning {}/{} services".format(remaining,
                                                            n_total)
        else:
            dmsg = ""
        self.deploy_button_label.set_text(dmsg)

        if self.showing_graph_split:
            bundle = self.placement_controller.bundle
            if self.show_scc_graph:
                gtext = scc_graph_for_bundle(bundle, self.metadata_controller)
            else:
                gtext = graph_for_bundle(bundle, self.metadata_controller)
            if gtext == "":
                gtext = "No graph to display yet."
            self.bundle_graph_text.set_text(gtext)

    def browse_maas(self, sender):

        bc = self.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.remove_overlay)
        self.show_overlay(w)

    def do_clear_all(self, sender):
        self.placement_controller.clear_all_assignments()

    def do_add_charm(self, charm_name, charm_dict):
        """Add new service and focus its widget.

        """
        assert(self.state == UIState.CHARMSTORE_VIEW)

        def done_cb(f):
            csid = CharmStoreID(charm_dict['Id'])
            id_no_rev = csid.as_str_without_rev()
            info = self.metadata_controller.get_charm_info(id_no_rev,
                                                           lambda _: None)
            is_subordinate = info["Meta"]["charm-metadata"].get(
                "Subordinate", False)
            service_name = self.placement_controller.add_new_service(
                charm_name, charm_dict, is_subordinate=is_subordinate)
            self.frame.focus_position = 'body'
            self.columns.focus_position = 0
            self.update()
            self.services_column.select_service(service_name)

        # TODO MMCC: need a 'loading' indicator to start here
        self.metadata_controller.load([charm_dict['Id']], done_cb)

    def do_add_bundle(self, bundle_dict):
        assert(self.state == UIState.CHARMSTORE_VIEW)
        _, new_services, _ = self.placement_controller.merge_bundle(
            bundle_dict)
        self.frame.focus_position = 'body'
        self.columns.focus_position = 0
        charms = list(set([s.charm_source for s in new_services]))
        self.metadata_controller.load(charms)
        self.update()
        ss = sorted(new_services,
                    key=attrgetter('service_name'))
        first_service = ss[0].service_name
        self.services_column.select_service(first_service)

    def do_clear_machine(self, sender, machine):
        self.placement_controller.clear_assignments(machine)

    def clear_selections(self):
        self.services_column.clear_selections()
        self.machines_column.clear_selections()

    def reset_selections(self, top=False):
        self.clear_selections()
        self.state = UIState.CHARMSTORE_VIEW
        self.update()
        self.columns.focus_position = 0

        if top:
            self.services_column.focus_top()
        else:
            self.services_column.focus_next()

    def focus_machines_column(self):
        self.columns.focus_position = 1
        self.machines_column.focus_prev_or_top()

    def focus_relations_column(self):
        self.columns.focus_position = 1
        self.relations_column.focus_prev_or_top()

    def focus_options_column(self):
        self.columns.focus_position = 1
        self.options_column.focus_prev_or_top()

    def focus_charmstore_column(self):
        self.columns.focus_position = 1
        self.charmstore_column.focus_prev_or_top()

    def edit_placement(self):
        self.state = UIState.PLACEMENT_EDITOR
        self.update()
        self.focus_machines_column()

    def show_default_view(self, *args):
        self.state = UIState.CHARMSTORE_VIEW
        self.update()

    def edit_relations(self, service):
        self.state = UIState.RELATION_EDITOR
        self.relations_column.set_service(service)
        self.update()
        self.focus_relations_column()

    def edit_options(self, service):
        self.state = UIState.OPTIONS_EDITOR
        self.options_column.set_service(service)
        self.update()
        self.focus_options_column()

    def do_deploy(self, sender):
        self.do_deploy_cb()

    def show_overlay(self, overlay_widget):
        if not self.showing_overlay:
            self.orig_w = self._w
        self._w = Overlay(top_w=overlay_widget,
                          bottom_w=self._w,
                          align='center',
                          width=('relative', 60),
                          min_width=80,
                          valign='middle',
                          height='pack')
        self.showing_overlay = True

    def remove_overlay(self, overlay_widget):
        # urwid note: we could also get orig_w as
        # self._w.contents[0][0], but this is clearer:
        self._w = self.orig_w
        self.showing_overlay = False
    def build_widgets(self):

        self.services_column = ServicesColumn(self.display_controller,
                                              self.placement_controller,
                                              self)

        self.machines_column = MachinesColumn(self.display_controller,
                                              self.placement_controller,
                                              self)
        self.relations_column = RelationsColumn(self.display_controller,
                                                self.placement_controller,
                                                self,
                                                self.metadata_controller)
        self.charmstore_column = CharmstoreColumn(self.display_controller,
                                                  self.placement_controller,
                                                  self,
                                                  self.metadata_controller)
        self.options_column = OptionsColumn(self.display_controller,
                                            self.placement_controller,
                                            self,
                                            self.metadata_controller)

        self.machines_header = self.get_machines_header(self.machines_column)
        self.relations_header = self.get_relations_header()
        self.services_header = self.get_services_header()
        self.charmstore_header = self.get_charmstore_header(
            self.charmstore_column)
        self.options_header = self.get_options_header(self.options_column)

        cs = [self.services_header, self.charmstore_header]

        self.header_columns = Columns(cs, dividechars=2)

        self.columns = Columns([self.services_column,
                                self.machines_column], dividechars=2)

        self.deploy_button = MenuSelectButton("\nCommit\n",
                                              on_press=self.do_deploy)
        self.deploy_button_label = Text("Some charms use default")
        self.placement_edit_body_pile = Pile([self.columns])
        self.placement_edit_body = Filler(Padding(
            self.placement_edit_body_pile,
            align='center',
            width=('relative', 95)),
            valign='top')
        self.bundle_graph_text = Text("No graph to display yet.")
        self.bundle_graph_widget = Padding(self.bundle_graph_text,
                                           'center', 'pack')
        b = AttrMap(self.deploy_button,
                    'frame_header',
                    'button_primary focus')
        self.footer_grid = GridFlow([self.deploy_button_label,
                                     Padding(b, width=28,
                                             align='center')],
                                    28, 1, 1, 'right')
        f = AttrMap(self.footer_grid,
                    'frame_footer',
                    'frame_footer')

        self.frame = Frame(header=Pile([self.header_columns, HR()]),
                           body=self.placement_edit_body,
                           footer=f)
        return self.frame
Ejemplo n.º 44
0
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)