def test_show_matching_filter(self, mock_machinewidget): self.mock_maas_state.machines.return_value = [ self.mock_machine, self.mock_machine2, self.mock_machine3 ] # a little extra work to ensure that calls to # MockWidget.__init__() return mocks with the intended machine # attribute set: mw1 = MagicMock(name="mw1") mw1.machine = self.mock_machine mw2 = MagicMock(name="mw2") mw2.machine = self.mock_machine2 mw3 = MagicMock(name="mw3") mw3.machine = self.mock_machine3 # the rest are the placeholders: mw4 = MagicMock(name="mock_placeholder_widget1") mw4.machine = self.pc.sub_placeholder mw5 = MagicMock(name="mock_placeholder_widget2") mw5.machine = self.pc.def_placeholder mock_machinewidget.side_effect = [mw1, mw2, mw3, mw4, mw5] ml = MachinesList(self.pc, self.actions) self.assertEqual(5, len(ml.machine_widgets)) ml.filter_string = "machine1-filter_label" ml.update() print("ml.machinewidgets is {}".format(ml.machine_widgets)) self.assertEqual(1, len(ml.machine_widgets))
def test_show_matching_filter(self, mock_machinewidget): self.mock_maas_state.machines.return_value = [self.mock_machine, self.mock_machine2, self.mock_machine3] # a little extra work to ensure that calls to # MockWidget.__init__() return mocks with the intended machine # attribute set: mw1 = MagicMock(name="mw1") mw1.machine = self.mock_machine mw2 = MagicMock(name="mw2") mw2.machine = self.mock_machine2 mw3 = MagicMock(name="mw3") mw3.machine = self.mock_machine3 # the rest are the placeholders: mw4 = MagicMock(name="mock_placeholder_widget1") mw4.machine = self.pc.sub_placeholder mw5 = MagicMock(name="mock_placeholder_widget2") mw5.machine = self.pc.def_placeholder mock_machinewidget.side_effect = [mw1, mw2, mw3, mw4, mw5] ml = MachinesList(self.pc, self.actions) self.assertEqual(5, len(ml.machine_widgets)) ml.filter_string = "machine1-filter_label" ml.update() print("ml.machinewidgets is {}".format(ml.machine_widgets)) self.assertEqual(1, len(ml.machine_widgets))
def build_widgets(self): def has_services_p(m): pc = self.placement_controller n = sum( [len(al) for at, al in pc.assignments_for_machine(m).items()]) return n > 0 clear_machine_func = self.placement_view.do_clear_machine show_chooser_func = self.placement_view.do_show_service_chooser self.open_maas_button = AttrMap( Button("Open in Browser", on_press=self.browse_maas), 'button_secondary', 'button_secondary focus') bc = self.placement_view.config.juju_env['bootstrap-config'] maasname = "'{}' <{}>".format(bc['name'], bc['maas-server']) maastitle = "Connected to MAAS {} l:root p:{}".format( maasname, self.placement_view.config.getopt('openstack_password')) tw = Columns([ Text(maastitle), Padding(self.open_maas_button, align='right', width=BUTTON_SIZE, right=2) ]) self.machines_list = MachinesList( self.placement_controller, [(has_services_p, 'Clear All Services', clear_machine_func), (has_services_p, 'Remove Some Services', show_chooser_func)], show_hardware=True, title_widgets=tw) self.machines_list.update() self.machines_list_pile = Pile([self.machines_list, Divider()]) # placeholders replaced in update() with absolute indexes, so # if you change this list, check update(). pl = [ Text(('subheading', "Machines"), align='center'), Divider(), Pile([]), # machines_list Divider() ] self.main_pile = Pile(pl) return self.main_pile
def test_widgets_config(self, mock_machinewidget): for show_hardware in [False, True]: for show_assignments in [False, True]: MachinesList(self.pc, self.actions, show_hardware=show_hardware, show_assignments=show_assignments) mock_machinewidget.assert_any_call(self.mock_machine, self.pc, self.actions, show_hardware, show_assignments) mock_machinewidget.reset_mock()
def build_widgets(self): if self.charm_class.allow_multi_units: machine_string = "machines" plural_string = "s" else: machine_string = "a machine" plural_string = "" instructions = Text("Select {} to host {}".format( machine_string, self.charm_class.display_name)) self.service_widget = ServiceWidget(self.charm_class, self.controller, show_constraints=True, show_placements=True) all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.do_select_baremetal), (AssignmentType.LXC, 'Add as LXC', self.do_select_lxc), (AssignmentType.KVM, 'Add as KVM', self.do_select_kvm)] actions = [(label, func) for atype, label, func in all_actions if atype in self.charm_class.allowed_assignment_types] constraints = self.charm_class.constraints # NOTE: show_assignments=False is a WORKAROUND for #194 self.machines_list = MachinesList(self.controller, actions, constraints=constraints, show_hardware=True, show_assignments=False) self.machines_list.update() close_button = AttrMap(Button('X', on_press=self.close_pressed), 'button_secondary', 'button_secondary focus') p = Pile([ GridFlow([close_button], 5, 1, 0, 'right'), instructions, Divider(), self.service_widget, Divider(), self.machines_list ]) return LineBox(p, title="Select Machine{}".format(plural_string))
def build_widgets(self): def has_services_p(m): pc = self.placement_controller n = sum([len(al) for at, al in pc.assignments_for_machine(m).items()]) return n > 0 clear_machine_func = self.placement_view.do_clear_machine show_chooser_func = self.placement_view.do_show_service_chooser self.open_maas_button = AttrMap(Button("Open in Browser", on_press=self.browse_maas), 'button_secondary', 'button_secondary focus') bc = self.placement_view.config.juju_env['bootstrap-config'] maasname = "'{}' <{}>".format(bc['name'], bc['maas-server']) maastitle = "Connected to MAAS {} l:root p:{}".format( maasname, self.placement_view.config.getopt('openstack_password')) tw = Columns([Text(maastitle), Padding(self.open_maas_button, align='right', width=BUTTON_SIZE, right=2)]) self.machines_list = MachinesList(self.placement_controller, [(has_services_p, 'Clear All Services', clear_machine_func), (has_services_p, 'Remove Some Services', show_chooser_func)], show_hardware=True, title_widgets=tw) self.machines_list.update() self.machines_list_pile = Pile([self.machines_list, Divider()]) # placeholders replaced in update() with absolute indexes, so # if you change this list, check update(). pl = [Text(('subheading', "Machines"), align='center'), Divider(), Pile([]), # machines_list Divider()] self.main_pile = Pile(pl) return self.main_pile
def build_widgets(self): if self.charm_class.allow_multi_units: machine_string = "machines" plural_string = "s" else: machine_string = "a machine" plural_string = "" instructions = Text("Select {} to host {}".format( machine_string, self.charm_class.display_name)) self.service_widget = ServiceWidget(self.charm_class, self.controller, show_constraints=True, show_placements=True) all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.do_select_baremetal), (AssignmentType.LXC, 'Add as LXC', self.do_select_lxc), (AssignmentType.KVM, 'Add as KVM', self.do_select_kvm)] actions = [(label, func) for atype, label, func in all_actions if atype in self.charm_class.allowed_assignment_types] constraints = self.charm_class.constraints # NOTE: show_assignments=False is a WORKAROUND for #194 self.machines_list = MachinesList(self.controller, actions, constraints=constraints, show_hardware=True, show_assignments=False) self.machines_list.update() close_button = AttrMap(Button('X', on_press=self.close_pressed), 'button_secondary', 'button_secondary focus') p = Pile([GridFlow([close_button], 5, 1, 0, 'right'), instructions, Divider(), self.service_widget, Divider(), self.machines_list]) return LineBox(p, title="Select Machine{}".format(plural_string))
class MachinesColumn(WidgetWrap): """Shows machines or a link to MAAS to add more""" def __init__(self, display_controller, placement_controller, placement_view): self.display_controller = display_controller self.placement_controller = placement_controller self.placement_view = placement_view w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): def has_services_p(m): pc = self.placement_controller n = sum( [len(al) for at, al in pc.assignments_for_machine(m).items()]) return n > 0 clear_machine_func = self.placement_view.do_clear_machine show_chooser_func = self.placement_view.do_show_service_chooser self.open_maas_button = AttrMap( Button("Open in Browser", on_press=self.browse_maas), 'button_secondary', 'button_secondary focus') bc = self.placement_view.config.juju_env['bootstrap-config'] maasname = "'{}' <{}>".format(bc['name'], bc['maas-server']) maastitle = "Connected to MAAS {}".format(maasname) tw = Columns([ Text(maastitle), Padding(self.open_maas_button, align='right', width=BUTTON_SIZE, right=2) ]) self.machines_list = MachinesList( self.placement_controller, [(has_services_p, 'Clear All Services', clear_machine_func), (has_services_p, 'Remove Some Services', show_chooser_func)], show_hardware=True, title_widgets=tw) self.machines_list.update() self.machines_list_pile = Pile([self.machines_list, Divider()]) # placeholders replaced in update() with absolute indexes, so # if you change this list, check update(). pl = [ Text(('subheading', "Machines"), align='center'), Divider(), Pile([]), # machines_list Divider() ] self.main_pile = Pile(pl) return self.main_pile def update(self): self.machines_list.update() bc = self.placement_view.config.juju_env['bootstrap-config'] empty_maas_msg = ("There are no available machines.\n" "Open {} to add machines to " "'{}':".format(bc['maas-server'], bc['name'])) self.empty_maas_widgets = Pile([ Text([('error_icon', "\N{WARNING SIGN} "), empty_maas_msg], align='center'), Padding(self.open_maas_button, align='center', width=BUTTON_SIZE) ]) # 1 machine is the subordinate placeholder: if len(self.placement_controller.machines()) == 1: self.main_pile.contents[2] = (self.empty_maas_widgets, self.main_pile.options()) else: self.main_pile.contents[2] = (self.machines_list_pile, self.main_pile.options()) def browse_maas(self, sender): bc = self.placement_view.config.juju_env['bootstrap-config'] try: p = Popen(["sensible-browser", bc['maas-server']], stdout=PIPE, stderr=PIPE) outs, errs = p.communicate(timeout=5) except TimeoutExpired: # went five seconds without an error, so we assume it's # OK. Don't kill it, just let it go: return e = errs.decode('utf-8') msg = "Error opening '{}' in a browser:\n{}".format(bc['name'], e) w = InfoDialog(msg, self.placement_view.remove_overlay) self.placement_view.show_overlay(w)
def test_hide_non_matching_constraints(self, mock_machinewidget): ml = MachinesList(self.pc, self.actions, {'cpu_cores': 16384}) self.assertEqual(0, len(ml.machine_widgets))
def test_show_matching_constraints(self, mock_machinewidget): ml = MachinesList(self.pc, self.actions, {'cpu_cores': 2}) self.assertEqual(1, len(ml.machine_widgets))
class MachineChooser(WidgetWrap): """Presents list of machines to assign a service to. Supports multiple selection if the service does. """ def __init__(self, controller, charm_class, parent_widget): self.controller = controller self.charm_class = charm_class self.parent_widget = parent_widget w = self.build_widgets() super().__init__(w) def build_widgets(self): if self.charm_class.allow_multi_units: machine_string = "machines" plural_string = "s" else: machine_string = "a machine" plural_string = "" instructions = Text("Select {} to host {}".format( machine_string, self.charm_class.display_name)) self.service_widget = ServiceWidget(self.charm_class, self.controller, show_constraints=True, show_placements=True) all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.do_select_baremetal), (AssignmentType.LXC, 'Add as LXC', self.do_select_lxc), (AssignmentType.KVM, 'Add as KVM', self.do_select_kvm)] actions = [(label, func) for atype, label, func in all_actions if atype in self.charm_class.allowed_assignment_types] constraints = self.charm_class.constraints # NOTE: show_assignments=False is a WORKAROUND for #194 self.machines_list = MachinesList(self.controller, actions, constraints=constraints, show_hardware=True, show_assignments=False) self.machines_list.update() close_button = AttrMap(Button('X', on_press=self.close_pressed), 'button_secondary', 'button_secondary focus') p = Pile([GridFlow([close_button], 5, 1, 0, 'right'), instructions, Divider(), self.service_widget, Divider(), self.machines_list]) return LineBox(p, title="Select Machine{}".format(plural_string)) def do_select_baremetal(self, sender, machine): self.do_select(sender, machine, AssignmentType.BareMetal) def do_select_lxc(self, sender, machine): self.do_select(sender, machine, AssignmentType.LXC) def do_select_kvm(self, sender, machine): self.do_select(sender, machine, AssignmentType.KVM) def do_select(self, sender, machine, atype): self.controller.assign(machine, self.charm_class, atype) self.machines_list.update() self.service_widget.update() self.parent_widget.remove_overlay(self) def close_pressed(self, sender): self.parent_widget.remove_overlay(self)
class MachinesColumn(WidgetWrap): """Shows machines or a link to MAAS to add more""" def __init__(self, display_controller, placement_controller, placement_view): self.display_controller = display_controller self.placement_controller = placement_controller self.placement_view = placement_view w = self.build_widgets() super().__init__(w) self.update() def selectable(self): return True def build_widgets(self): def has_services_p(m): pc = self.placement_controller n = sum([len(al) for at, al in pc.assignments_for_machine(m).items()]) return n > 0 clear_machine_func = self.placement_view.do_clear_machine show_chooser_func = self.placement_view.do_show_service_chooser self.open_maas_button = AttrMap(Button("Open in Browser", on_press=self.browse_maas), 'button_secondary', 'button_secondary focus') bc = self.placement_view.config.juju_env['bootstrap-config'] maasname = "'{}' <{}>".format(bc['name'], bc['maas-server']) maastitle = "Connected to MAAS {} l:root p:{}".format( maasname, self.placement_view.config.getopt('openstack_password')) tw = Columns([Text(maastitle), Padding(self.open_maas_button, align='right', width=BUTTON_SIZE, right=2)]) self.machines_list = MachinesList(self.placement_controller, [(has_services_p, 'Clear All Services', clear_machine_func), (has_services_p, 'Remove Some Services', show_chooser_func)], show_hardware=True, title_widgets=tw) self.machines_list.update() self.machines_list_pile = Pile([self.machines_list, Divider()]) # placeholders replaced in update() with absolute indexes, so # if you change this list, check update(). pl = [Text(('subheading', "Machines"), align='center'), Divider(), Pile([]), # machines_list Divider()] self.main_pile = Pile(pl) return self.main_pile def update(self): self.machines_list.update() bc = self.placement_view.config.juju_env['bootstrap-config'] empty_maas_msg = ("There are no available machines.\n" "Open {} to add machines to " "'{}':".format(bc['maas-server'], bc['name'])) self.empty_maas_widgets = Pile([Text([('error_icon', "\N{WARNING SIGN} "), empty_maas_msg], align='center'), Padding(self.open_maas_button, align='center', width=BUTTON_SIZE)]) # 1 machine is the subordinate placeholder: if len(self.placement_controller.machines()) == 1: self.main_pile.contents[2] = (self.empty_maas_widgets, self.main_pile.options()) else: self.main_pile.contents[2] = (self.machines_list_pile, self.main_pile.options()) def browse_maas(self, sender): bc = self.placement_view.config.juju_env['bootstrap-config'] try: p = Popen(["sensible-browser", bc['maas-server']], stdout=PIPE, stderr=PIPE) outs, errs = p.communicate(timeout=5) except TimeoutExpired: # went five seconds without an error, so we assume it's # OK. Don't kill it, just let it go: return e = errs.decode('utf-8') msg = "Error opening '{}' in a browser:\n{}".format(bc['name'], e) w = InfoDialog(msg, self.placement_view.remove_overlay) self.placement_view.show_overlay(w)
class MachineChooser(WidgetWrap): """Presents list of machines to assign a service to. Supports multiple selection if the service does. """ def __init__(self, controller, charm_class, parent_widget): self.controller = controller self.charm_class = charm_class self.parent_widget = parent_widget w = self.build_widgets() super().__init__(w) def build_widgets(self): if self.charm_class.allow_multi_units: machine_string = "machines" plural_string = "s" else: machine_string = "a machine" plural_string = "" instructions = Text("Select {} to host {}".format( machine_string, self.charm_class.display_name)) self.service_widget = ServiceWidget(self.charm_class, self.controller, show_constraints=True, show_placements=True) all_actions = [(AssignmentType.BareMetal, 'Add as Bare Metal', self.do_select_baremetal), (AssignmentType.LXC, 'Add as LXC', self.do_select_lxc), (AssignmentType.KVM, 'Add as KVM', self.do_select_kvm)] actions = [(label, func) for atype, label, func in all_actions if atype in self.charm_class.allowed_assignment_types] constraints = self.charm_class.constraints # NOTE: show_assignments=False is a WORKAROUND for #194 self.machines_list = MachinesList(self.controller, actions, constraints=constraints, show_hardware=True, show_assignments=False) self.machines_list.update() close_button = AttrMap(Button('X', on_press=self.close_pressed), 'button_secondary', 'button_secondary focus') p = Pile([ GridFlow([close_button], 5, 1, 0, 'right'), instructions, Divider(), self.service_widget, Divider(), self.machines_list ]) return LineBox(p, title="Select Machine{}".format(plural_string)) def do_select_baremetal(self, sender, machine): self.do_select(sender, machine, AssignmentType.BareMetal) def do_select_lxc(self, sender, machine): self.do_select(sender, machine, AssignmentType.LXC) def do_select_kvm(self, sender, machine): self.do_select(sender, machine, AssignmentType.KVM) def do_select(self, sender, machine, atype): self.controller.assign(machine, self.charm_class, atype) self.machines_list.update() self.service_widget.update() self.parent_widget.remove_overlay(self) def close_pressed(self, sender): self.parent_widget.remove_overlay(self)