def __init__(self, juju_state): table = Table() table.addHeadings([ Text('Service'), Text('Hardware'), Text('Hostname'), Text('Machine'), ]) for name, service in juju_state['Services'].items(): s = ServiceWidget(name, service) q(s) for u in s.Units: m = MachineWidget( juju_state['Machines'][u.Machine.get_text()[0]]) table.addColumns(u.Name.get_text()[0], [u.Name, m.Hardware, m.DNSName, u.Machine]) super().__init__(body=table.render())
def __init__(self, machine_view): table = Table() table.addHeadings([ Text('Hostname'), Text('CPU'), Text('Storage'), Text('Memory'), ]) for m in machine_view: m = MachineWidget(m) table.addColumns(m.hostname, [ m.hostname, m.cpu_count, m.storage, m.memory ]) super().__init__(body=table.render())
def __init__(self, juju_state): table = Table() table.addHeadings([ Text('Service'), Text('Hardware'), Text('Hostname'), Text('Machine'), ]) for name, service in juju_state['Services'].items(): s = ServiceWidget(name, service) q(s) for u in s.Units: m = MachineWidget(juju_state['Machines'][u.Machine.get_text()[0]]) table.addColumns(u.Name.get_text()[0], [ u.Name, m.Hardware, m.DNSName, u.Machine ]) super().__init__(body=table.render())
class DeployStatusView(WidgetWrap): def __init__(self, app): self.app = app self.deployed = {} self.unit_w = None self.table = Table() super().__init__(Padding.center_80(self.table.render())) def refresh_nodes(self): """Adds services to the view if they don't already exist Schedules UI update on main thread to avoid urwid issues with changing listbox state during render. """ EventLoop.loop.event_loop._loop.call_soon_threadsafe( self._refresh_nodes_on_main_thread) def _refresh_nodes_on_main_thread(self): status = model_status() for name, service in sorted(status['applications'].items()): service_w = ServiceWidget(name, service) for unit in service_w.Units: try: unit_w = self.deployed[unit._name] except: self.deployed[unit._name] = unit unit_w = self.deployed[unit._name] self.table.addColumns( unit._name, [ ('fixed', 3, getattr(unit_w, 'Icon')), ('fixed', 50, getattr(unit_w, 'Name')), ('fixed', 20, getattr(unit_w, 'AgentStatus')) ] ) if not hasattr(unit_w, 'WorkloadInfo'): continue self.table.addColumns( unit._name, [ ('fixed', 5, Text("")), Color.info_context( unit_w.WorkloadInfo) ], force=True) self.update_ui_state(unit_w, unit._unit) def status_icon_state(self, agent_state): if agent_state == "maintenance" \ or agent_state == "allocating" \ or agent_state == "executing": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif agent_state == "waiting": status = ("pending_icon", "\N{HOURGLASS}") elif agent_state == "idle" \ or agent_state == "active": status = ("success_icon", "\u2713") elif agent_state == "blocked": status = ("error_icon", "\N{BLACK FLAG}") elif agent_state == "unknown": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, unit_w, unit): """ Updates individual machine information Arguments: service: current service unit_w: UnitInfo widget unit: current unit for service """ try: unit_w.Machine.set_text(unit.get('machine', '-')) unit_w.PublicAddress.set_text(unit['public-address']) unit_w.WorkloadInfo.set_text(unit['workload-status']['info']) if unit['workload-status']['status'] != 'unknown': unit_w.AgentStatus.set_text(unit['workload-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['workload-status']['status'])) else: unit_w.AgentStatus.set_text(unit['agent-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['agent-status']['status'])) except Exception as e: self.app.log.exception(e) self.app.ui.show_exception_message(e)
class ServicesView(WidgetWrap): view_columns = [ ('Icon', "", 2), ('Name', "Service", 0), ('AgentStatus', "Status", 20), ('PublicAddress', "IP", 20), ('Machine', "Machine", 20), ] def __init__(self, app): self.app = app self.deployed = {} self.unit_w = None self.table = Table() headings = [] for key, label, width in self.view_columns: # If no width assume ('weight', 1, widget) if width == 0: headings.append(Color.column_header(Text(label))) else: headings.append( ('fixed', width, Color.column_header(Text(label)))) self.table.addHeadings(headings) super().__init__(self.table.render()) self.refresh_nodes() def refresh_nodes(self): """ Adds services to the view if they don't already exist """ status = model_status() for name, service in sorted(status['applications'].items()): service_w = ServiceWidget(name, service) for unit in service_w.Units: services_list = [] try: unit_w = self.deployed[unit._name] except: self.deployed[unit._name] = unit unit_w = self.deployed[unit._name] for k, label, width in self.view_columns: if width == 0: services_list.append(getattr(unit_w, k)) else: if not hasattr(unit_w, k): continue services_list.append( ('fixed', width, getattr(unit_w, k))) self.table.addColumns(unit._name, services_list) if not hasattr(unit_w, 'WorkloadInfo'): continue self.table.addColumns( unit._name, [('fixed', 5, Text("")), Color.info_context(unit_w.WorkloadInfo)], force=True) self.update_ui_state(unit_w, unit._unit) def status_icon_state(self, agent_state): if agent_state == "maintenance" \ or agent_state == "allocating" \ or agent_state == "executing": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif agent_state == "waiting": status = ("pending_icon", "\N{HOURGLASS}") elif agent_state == "idle" \ or agent_state == "active": status = ("success_icon", "\u2713") elif agent_state == "blocked": status = ("error_icon", "\N{BLACK FLAG}") elif agent_state == "unknown": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, unit_w, unit): """ Updates individual machine information Arguments: service: current service unit_w: UnitInfo widget unit: current unit for service """ try: unit_w.Machine.set_text(unit.get('machine', '-')) unit_w.PublicAddress.set_text(unit['public-address']) unit_w.WorkloadInfo.set_text(unit['workload-status']['info']) if unit['workload-status']['status'] != 'unknown': unit_w.AgentStatus.set_text(unit['workload-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['workload-status']['status'])) else: unit_w.AgentStatus.set_text(unit['agent-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['agent-status']['status'])) except Exception as e: self.app.log.exception(e) self.app.ui.show_exception_message(e)
class ServicesView(WidgetWrap): view_columns = [('icon', "", 2), ('display_name', "Service", 0), ('agent_state', "Status", 12), ('public_address', "IP", 12), ('machine', "Machine", 12), ('container', "Container", 12), ('arch', "Arch ", 12), ('cpu_cores', "Cores", 12), ('mem', "Mem ", 12), ('storage', "Storage", 12)] def __init__(self, nodes, juju_state, maas_state, config): self.deployed = {} self.nodes = [] if nodes is None else nodes self.juju_state = juju_state self.maas_state = maas_state self.config = config self.unit_w = None self.log_cache = None self.table = Table() headings = [] for key, label, width in self.view_columns: # If no width assume ('weight', 1, widget) if width == 0: headings.append(Color.column_header(Text(label))) else: headings.append( ('fixed', width, Color.column_header(Text(label)))) self.table.addHeadings(headings) super().__init__(self.table.render()) self.refresh_nodes(self.nodes) def refresh_nodes(self, nodes): """ Adds services to the view if they don't already exist """ for node in nodes: services_list = [] charm_class, service = node if len(service.units) > 0: for u in sorted(service.units, key=attrgetter('unit_name')): # Refresh any state changes try: unit_w = self.deployed[u.unit_name] except: hwinfo = self._get_hardware_info(u) self.deployed[u.unit_name] = UnitInfoWidget( u, charm_class, hwinfo) unit_w = self.deployed[u.unit_name] for k, label, width in self.view_columns: if width == 0: services_list.append(getattr(unit_w, k)) else: services_list.append( ('fixed', width, getattr(unit_w, k))) self.table.addColumns(u.unit_name, services_list) self.table.addColumns( u.unit_name, [('fixed', 5, Text("")), Color.frame_subheader(unit_w.workload_info)], force=True) self.update_ui_state(charm_class, u, unit_w) def status_icon_state(self, charm_class, unit): # unit.agent_state may be "pending" despite errors elsewhere, # so we check for error_info first. # if the agent_state is "error", _detect_errors returns that. error_info = self._detect_errors(unit, charm_class) if error_info: status = ("error_icon", "\N{TETRAGRAM FOR FAILURE}") elif unit.agent_state == "pending": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif unit.agent_state == "installed": status = ("pending_icon", "\N{HOURGLASS}") elif unit.agent_state == "started": status = ("success_icon", "\u2713") elif unit.agent_state == "stopped": status = ("error_icon", "\N{BLACK FLAG}") elif unit.agent_state == "down": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, charm_class, unit, unit_w): """ Updates individual machine information """ unit_w.public_address.set_text(unit.public_address) unit_w.agent_state.set_text(unit.agent_state) unit_w.icon.set_text(self.status_icon_state(charm_class, unit)) # Special additional status text for these services if 'glance-simplestreams-sync' in unit.unit_name: status_oneline = get_sync_status().replace("\n", " - ") unit_w.workload_info.set_text(status_oneline) elif unit.is_horizon and unit.agent_state == "started": unit_w.workload_info.set_text( "Login: https://{}/horizon " "l:{} p:{}".format(unit.public_address, 'ubuntu', self.config.getopt('openstack_password'))) elif unit.is_jujugui and unit.agent_state == "started": unit_w.workload_info.set_text("Login: https://{}/".format( unit.public_address)) else: unit_w.workload_info.set_text(" {} - {}".format( unit.extended_agent_state, unit.workload_info)) def _get_hardware_info(self, unit): """Get hardware info from juju or maas Returns list of text and formatting tuples """ juju_machine = self.juju_state.machine(unit.machine_id) maas_machine = None if self.maas_state: maas_machine = self.maas_state.machine(juju_machine.instance_id) m = juju_machine if juju_machine.arch == "N/A": if maas_machine: m = maas_machine else: try: return self._get_container_info(unit) except: log.exception( "failed to get container info for unit {}.".format( unit)) hw_info = self._hardware_info_for_machine(m) hw_info['machine'] = juju_machine.machine_id return hw_info def _get_container_info(self, unit): """Attempt to get hardware info of host machine for a unit that looks like a container. """ base_machine = self.juju_state.base_machine(unit.machine_id) if base_machine.arch == "N/A" and self.maas_state is not None: m = self.maas_state.machine(base_machine.instance_id) else: m = base_machine # FIXME: Breaks single install status display # base_id, container_type, container_id = unit.machine_id.split('/') # ctypestr = dict(kvm="VM", lxc="Container")[container_type] # rl = ["{} {} (Machine {}".format(ctypestr, container_id, # base_id)] try: container_id = unit.machine_id.split('/')[-1] except: log.exception("ERROR: base_machine is {} and m is {}, " "and unit.machine_id is {}".format( base_machine, m, unit.machine_id)) return "?" base_id = base_machine.machine_id hw_info = self._hardware_info_for_machine(m) hw_info['machine'] = base_id hw_info['container'] = container_id return hw_info def _hardware_info_for_machine(self, m): return { "arch": m.arch, "cpu_cores": m.cpu_cores, "mem": m.mem, "storage": m.storage, "container": '-', "machine": 0 } def _detect_errors(self, unit, charm_class): """Look in multiple places for an error. Return error info string if present, or None if no error is found """ unit_machine = self.juju_state.machine(unit.machine_id) if unit.agent_state == "error": return unit.agent_state_info.lstrip() err_info = "" if unit.agent_state == 'pending' and \ unit_machine.agent_state is '' and \ unit_machine.agent_state_info is not None: # detect MAAS API errors, returned as 409 conflict: if "409" in unit_machine.agent_state_info: if charm_class.constraints is not None: err_info = "Found no machines meeting constraints: " err_info += ', '.join([ "{}='{}'".format(k, v) for k, v in charm_class.constraints.items() ]) else: err_info += "No machines available for unit." else: err_info += unit_machine.agent_state_info return err_info return None def get_log_text(self, unit_name): name = '-'.join(unit_name.split('/')) cmd = ("sudo grep {unit} /var/log/juju-ubuntu-local/all-machines.log " " | tail -n 2") cmd = cmd.format(unit=name) out = utils.get_command_output(cmd) if out['status'] == 0 and len(out['output']) > 0: return out['output'] else: return "No log matches for {}".format(name)
class DeployStatusView(WidgetWrap): def __init__(self, app): self.app = app self.deployed = {} self.unit_w = None self.table = Table() super().__init__(Padding.center_80(self.table.render())) def refresh_nodes(self, applications): """Adds services to the view if they don't already exist Schedules UI update on main thread to avoid urwid issues with changing listbox state during render. """ for name, application in sorted(applications.items()): # XXX refactor ubuntui to accept libjuju objects directly service = { 'units': { unit.name: { 'public-address': unit.public_address, 'machine': unit.machine_id, 'agent-status': { 'status': unit.agent_status, 'info': unit.agent_status_message, }, 'workload-status': { 'status': unit.workload_status, 'info': unit.workload_status_message, }, } for unit in application.units } } service_w = ServiceWidget(application.name, service) for unit in service_w.Units: try: unit_w = self.deployed[unit._name] except: self.deployed[unit._name] = unit unit_w = self.deployed[unit._name] self.table.addColumns( unit._name, [ ('fixed', 3, getattr(unit_w, 'Icon')), ('fixed', 50, getattr(unit_w, 'Name')), ('fixed', 20, getattr(unit_w, 'AgentStatus')) ] ) if not hasattr(unit_w, 'WorkloadInfo'): continue self.table.addColumns( unit._name, [ ('fixed', 5, Text("")), Color.info_context( unit_w.WorkloadInfo) ], force=True) self.update_ui_state(unit_w, unit._unit) def status_icon_state(self, agent_state): if agent_state == "maintenance" \ or agent_state == "allocating" \ or agent_state == "executing": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif agent_state == "waiting": status = ("pending_icon", "\N{HOURGLASS}") elif agent_state == "idle" \ or agent_state == "active": status = ("success_icon", "\u2713") elif agent_state == "blocked": status = ("error_icon", "\N{BLACK FLAG}") elif agent_state == "unknown": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, unit_w, unit): """ Updates individual machine information Arguments: service: current service unit_w: UnitInfo widget unit: current unit for service """ try: unit_w.Machine.set_text(unit.get('machine', '-')) unit_w.PublicAddress.set_text(unit['public-address']) unit_w.WorkloadInfo.set_text(unit['workload-status']['info']) if unit['workload-status']['status'] != 'unknown': unit_w.AgentStatus.set_text(unit['workload-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['workload-status']['status'])) else: unit_w.AgentStatus.set_text(unit['agent-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['agent-status']['status'])) except Exception: raise
class ServicesView(WidgetWrap): view_columns = [ ('Icon', "", 2), ('Name', "Service", 0), ('AgentStatus', "Status", 20), ('PublicAddress', "IP", 20), ('Machine', "Machine", 20), ] def __init__(self, app): self.app = app self.deployed = {} self.unit_w = None self.table = Table() headings = [] for key, label, width in self.view_columns: # If no width assume ('weight', 1, widget) if width == 0: headings.append(Color.column_header(Text(label))) else: headings.append( ('fixed', width, Color.column_header(Text(label)))) self.table.addHeadings(headings) super().__init__(self.table.render()) self.refresh_nodes() def refresh_nodes(self): """ Adds services to the view if they don't already exist """ status = model_status() for name, service in sorted(status['applications'].items()): service_w = ServiceWidget(name, service) for unit in service_w.Units: services_list = [] try: unit_w = self.deployed[unit._name] except: self.deployed[unit._name] = unit unit_w = self.deployed[unit._name] for k, label, width in self.view_columns: if width == 0: services_list.append(getattr(unit_w, k)) else: if not hasattr(unit_w, k): continue services_list.append(('fixed', width, getattr(unit_w, k))) self.table.addColumns(unit._name, services_list) if not hasattr(unit_w, 'WorkloadInfo'): continue self.table.addColumns( unit._name, [ ('fixed', 5, Text("")), Color.info_context( unit_w.WorkloadInfo) ], force=True) self.update_ui_state(unit_w, unit._unit) def status_icon_state(self, agent_state): if agent_state == "maintenance" \ or agent_state == "allocating" \ or agent_state == "executing": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif agent_state == "waiting": status = ("pending_icon", "\N{HOURGLASS}") elif agent_state == "idle" \ or agent_state == "active": status = ("success_icon", "\u2713") elif agent_state == "blocked": status = ("error_icon", "\N{BLACK FLAG}") elif agent_state == "unknown": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, unit_w, unit): """ Updates individual machine information Arguments: service: current service unit_w: UnitInfo widget unit: current unit for service """ try: unit_w.Machine.set_text(unit.get('machine', '-')) unit_w.PublicAddress.set_text(unit['public-address']) unit_w.WorkloadInfo.set_text(unit['workload-status']['info']) if unit['workload-status']['status'] != 'unknown': unit_w.AgentStatus.set_text(unit['workload-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['workload-status']['status'])) else: unit_w.AgentStatus.set_text(unit['agent-status']['status']) unit_w.Icon.set_text( self.status_icon_state(unit['agent-status']['status'])) except Exception as e: self.app.log.exception(e) self.app.ui.show_exception_message(e)
class StepsView(WidgetWrap): INPUT_TYPES = { 'text': StringEditor(), 'password': PasswordEditor(), 'boolean': YesNo(), 'integer': IntegerEditor() } def __init__(self, app, steps, cb=None): """ init Arguments: cb: process step callback """ self.app = app self.table = Table() self.cb = cb self.steps_queue = steps for step_model in self.steps_queue: step_widget = self.add_step_widget(step_model) self.table.addColumns( step_model.path, [ ('fixed', 3, step_widget['icon']), step_widget['description'], ] ) # Need to still prompt for the user to submit # even though no questions are asked if len(step_widget['additional_input']) == 0: self.table.addRow( Padding.right_20( Color.button_primary( submit_btn(on_press=self.submit, user_data=(step_model, step_widget)), focus_map='button_primary focus')), False) for i in step_widget['additional_input']: self.table.addRow(Padding.line_break(""), False) self.table.addColumns( step_model.path, [ ('weight', 0.5, Padding.left(i['label'], left=5)), ('weight', 1, Color.string_input( i['input'], focus_map='string_input focus')), ], force=True ) self.table.addRow( Padding.right_20( Color.button_primary( submit_btn( on_press=self.submit, user_data=(step_model, step_widget)), focus_map='button_primary focus')), False) self.table.addRow(Padding.line_break(""), False) self.table.addRow(Padding.center_20( Color.button_primary( done_btn(on_press=self.done, label="View Summary"), focus_map='button_primary focus'))) super().__init__(Padding.center_80(self.table.render())) def add_step_widget(self, step_model): if not step_model.viewable: self.app.log.debug("{} is not viewable, skipping".format( step_model)) return step_widget_dict = {'title': Text(step_model.title), 'description': Text(step_model.description), 'result': Text(step_model.result), 'icon': Text(step_model.icon)} step_widget_dict['additional_input'] = [] if len(step_model.additional_input) > 0: for i in step_model.additional_input: widget = { "label": Text(i['label']), "key": i['key'], "input": self.INPUT_TYPES.get(i['type']) } if 'default' in i: widget['input'] = StringEditor(default=i['default']) step_widget_dict['additional_input'].append(widget) return step_widget_dict def done(self, *args): self.cb({}, done=True) def submit(self, btn, stepmodel_stepwidget): step_model, step_widget = stepmodel_stepwidget # set icon step_model.icon = step_widget['icon'] # merge the step_widget input data into our step model for i in step_model.additional_input: try: matching_widget = [x for x in step_widget['additional_input'] if x['key'] == i['key']][0] i['input'] = matching_widget['input'].value except IndexError as e: self.app.log.error( "Tried to pull a value from an " "invalid input: {}/{}".format(e, matching_widget)) self.cb(step_model) def update_icon_state(self, icon, result_code): """ updates status icon Arguments: icon: icon widget result_code: 3 types of results, error, waiting, complete """ if result_code == "error": icon.set_text( ("error_icon", "\N{BLACK FLAG}")) elif result_code == "waiting": icon.set_text(("pending_icon", "\N{HOURGLASS}")) elif result_code == "active": icon.set_text(("success_icon", "\N{BALLOT BOX WITH CHECK}")) else: # NOTE: Should not get here, if we do make sure we account # for that error type above. icon.set_text(("error_icon", "?"))
class ServicesView(WidgetWrap): view_columns = [ ('icon', "", 2), ('display_name', "Service", 0), ('agent_state', "Status", 12), ('public_address', "IP", 12), ('machine', "Machine", 12), ('container', "Container", 12), ('arch', "Arch ", 12), ('cpu_cores', "Cores", 12), ('mem', "Mem ", 12), ('storage', "Storage", 12) ] def __init__(self, nodes, juju_state, maas_state, config): self.deployed = {} self.nodes = [] if nodes is None else nodes self.juju_state = juju_state self.maas_state = maas_state self.config = config self.unit_w = None self.log_cache = None self.table = Table() headings = [] for key, label, width in self.view_columns: # If no width assume ('weight', 1, widget) if width == 0: headings.append(Color.column_header(Text(label))) else: headings.append( ('fixed', width, Color.column_header(Text(label)))) self.table.addHeadings(headings) super().__init__(self.table.render()) self.refresh_nodes(self.nodes) def refresh_nodes(self, nodes): """ Adds services to the view if they don't already exist """ for node in nodes: services_list = [] charm_class, service = node if len(service.units) > 0: for u in sorted(service.units, key=attrgetter('unit_name')): # Refresh any state changes try: unit_w = self.deployed[u.unit_name] except: hwinfo = self._get_hardware_info(u) self.deployed[u.unit_name] = UnitInfoWidget( u, charm_class, hwinfo) unit_w = self.deployed[u.unit_name] for k, label, width in self.view_columns: if width == 0: services_list.append(getattr(unit_w, k)) else: services_list.append(('fixed', width, getattr(unit_w, k))) self.table.addColumns(u.unit_name, services_list) self.table.addColumns( u.unit_name, [ ('fixed', 5, Text("")), Color.frame_subheader(unit_w.workload_info) ], force=True) self.update_ui_state(charm_class, u, unit_w) def status_icon_state(self, charm_class, unit): # unit.agent_state may be "pending" despite errors elsewhere, # so we check for error_info first. # if the agent_state is "error", _detect_errors returns that. error_info = self._detect_errors(unit, charm_class) if error_info: status = ("error_icon", "\N{TETRAGRAM FOR FAILURE}") elif unit.agent_state == "pending": pending_status = [("pending_icon", "\N{CIRCLED BULLET}"), ("pending_icon", "\N{CIRCLED WHITE BULLET}"), ("pending_icon", "\N{FISHEYE}")] status = random.choice(pending_status) elif unit.agent_state == "installed": status = ("pending_icon", "\N{HOURGLASS}") elif unit.agent_state == "started": status = ("success_icon", "\u2713") elif unit.agent_state == "stopped": status = ("error_icon", "\N{BLACK FLAG}") elif unit.agent_state == "down": status = ("error_icon", "\N{DOWNWARDS BLACK ARROW}") else: # NOTE: Should not get here, if we do make sure we account # for that error type above. status = ("error_icon", "?") return status def update_ui_state(self, charm_class, unit, unit_w): """ Updates individual machine information """ unit_w.public_address.set_text(unit.public_address) unit_w.agent_state.set_text(unit.agent_state) unit_w.icon.set_text(self.status_icon_state(charm_class, unit)) # Special additional status text for these services if 'glance-simplestreams-sync' in unit.unit_name: status_oneline = get_sync_status().replace("\n", " - ") unit_w.workload_info.set_text(status_oneline) elif unit.is_horizon and unit.agent_state == "started": unit_w.workload_info.set_text( "Login: https://{}/horizon " "l:{} p:{}".format( unit.public_address, 'ubuntu', self.config.getopt('openstack_password'))) elif unit.is_jujugui and unit.agent_state == "started": unit_w.workload_info.set_text( "Login: https://{}/".format( unit.public_address)) else: unit_w.workload_info.set_text( " {} - {}".format(unit.extended_agent_state, unit.workload_info)) def _get_hardware_info(self, unit): """Get hardware info from juju or maas Returns list of text and formatting tuples """ juju_machine = self.juju_state.machine(unit.machine_id) maas_machine = None if self.maas_state: maas_machine = self.maas_state.machine(juju_machine.instance_id) m = juju_machine if juju_machine.arch == "N/A": if maas_machine: m = maas_machine else: try: return self._get_container_info(unit) except: log.exception( "failed to get container info for unit {}.".format( unit)) hw_info = self._hardware_info_for_machine(m) hw_info['machine'] = juju_machine.machine_id return hw_info def _get_container_info(self, unit): """Attempt to get hardware info of host machine for a unit that looks like a container. """ base_machine = self.juju_state.base_machine(unit.machine_id) if base_machine.arch == "N/A" and self.maas_state is not None: m = self.maas_state.machine(base_machine.instance_id) else: m = base_machine # FIXME: Breaks single install status display # base_id, container_type, container_id = unit.machine_id.split('/') # ctypestr = dict(kvm="VM", lxc="Container")[container_type] # rl = ["{} {} (Machine {}".format(ctypestr, container_id, # base_id)] try: container_id = unit.machine_id.split('/')[-1] except: log.exception("ERROR: base_machine is {} and m is {}, " "and unit.machine_id is {}".format( base_machine, m, unit.machine_id)) return "?" base_id = base_machine.machine_id hw_info = self._hardware_info_for_machine(m) hw_info['machine'] = base_id hw_info['container'] = container_id return hw_info def _hardware_info_for_machine(self, m): return {"arch": m.arch, "cpu_cores": m.cpu_cores, "mem": m.mem, "storage": m.storage, "container": '-', "machine": 0} def _detect_errors(self, unit, charm_class): """Look in multiple places for an error. Return error info string if present, or None if no error is found """ unit_machine = self.juju_state.machine(unit.machine_id) if unit.agent_state == "error": return unit.agent_state_info.lstrip() err_info = "" if unit.agent_state == 'pending' and \ unit_machine.agent_state is '' and \ unit_machine.agent_state_info is not None: # detect MAAS API errors, returned as 409 conflict: if "409" in unit_machine.agent_state_info: if charm_class.constraints is not None: err_info = "Found no machines meeting constraints: " err_info += ', '.join(["{}='{}'".format(k, v) for k, v in charm_class.constraints.items()]) else: err_info += "No machines available for unit." else: err_info += unit_machine.agent_state_info return err_info return None def get_log_text(self, unit_name): name = '-'.join(unit_name.split('/')) cmd = ("sudo grep {unit} /var/log/juju-ubuntu-local/all-machines.log " " | tail -n 2") cmd = cmd.format(unit=name) out = utils.get_command_output(cmd) if out['status'] == 0 and len(out['output']) > 0: return out['output'] else: return "No log matches for {}".format(name)