From 627c033be5d34e78aa4012ec4975eacf994ed999 Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 17:18:18 +0000 Subject: [PATCH 1/7] New structure --- .github/workflows/test-xjs.yml | 25 +++++----- pyproject.toml | 27 +++++++++++ setup.py | 46 ------------------- xjs/__init__.py | 2 + xjs/__main__.py | 2 + application.py => xjs/application.py | 4 +- basicmachine.py => xjs/basicmachine.py | 4 +- basicunit.py => xjs/basicunit.py | 2 +- xjs => xjs/cli.py | 12 ++--- colors.py => xjs/colors.py | 0 container.py => xjs/container.py | 4 +- controller.py => xjs/controller.py | 0 machine.py => xjs/machine.py | 4 +- model.py => xjs/model.py | 2 +- .../networkinterface.py | 2 +- relation.py => xjs/relation.py | 2 +- subordinateunit.py => xjs/subordinateunit.py | 2 +- unit.py => xjs/unit.py | 4 +- 18 files changed, 64 insertions(+), 80 deletions(-) create mode 100644 pyproject.toml delete mode 100755 setup.py create mode 100644 xjs/__init__.py create mode 100644 xjs/__main__.py rename application.py => xjs/application.py (99%) rename basicmachine.py => xjs/basicmachine.py (98%) rename basicunit.py => xjs/basicunit.py (99%) rename xjs => xjs/cli.py (99%) mode change 100755 => 100644 rename colors.py => xjs/colors.py (100%) rename container.py => xjs/container.py (97%) rename controller.py => xjs/controller.py (100%) rename machine.py => xjs/machine.py (98%) rename model.py => xjs/model.py (99%) rename networkinterface.py => xjs/networkinterface.py (99%) rename relation.py => xjs/relation.py (98%) rename subordinateunit.py => xjs/subordinateunit.py (98%) rename unit.py => xjs/unit.py (97%) diff --git a/.github/workflows/test-xjs.yml b/.github/workflows/test-xjs.yml index ae318e9..58bbc8b 100644 --- a/.github/workflows/test-xjs.yml +++ b/.github/workflows/test-xjs.yml @@ -24,19 +24,18 @@ jobs: python -m pip install --upgrade pip pip install flake8 pycodestyle pip install -r requirements.txt - pycodestyle xjs - flake8 xjs + pycodestyle xjs/ + flake8 xjs/ - name: Run xjs tests run: | - python3 xjs examples/example.json - python3 xjs examples/example.json - python3 xjs examples/example.yaml - python3 xjs examples/example2.yaml - python3 xjs examples/example.json examples/example2.json - python3 xjs --application glance examples/example.json - python3 xjs --application rabbitmq-server examples/example.json - python3 xjs --model k8s examples/example2.json - python3 xjs --controller controller examples/example.json examples/example2.json - python3 xjs --unit k8s/0 examples/example2.json - python3 xjs --subordinate ovn-chassis/0 examples/example.json + python3 -m xjs examples/example.json + python3 -m xjs examples/example.yaml + python3 -m xjs examples/example2.yaml + python3 -m xjs examples/example.json examples/example2.json + python3 -m xjs --application glance examples/example.json + python3 -m xjs --application rabbitmq-server examples/example.json + python3 -m xjs --model k8s examples/example2.json + python3 -m xjs --controller controller examples/example.json examples/example2.json + python3 -m xjs --unit k8s/0 examples/example2.json + python3 -m xjs --subordinate ovn-chassis/0 examples/example.json diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f9633fa --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" + +[project] +name = "xjs" +version = "0.1" +description = "A tool to parse and display offline juju status files" +license = "GPL-3.0-only" +requires-python = ">=3.12" +dependencies = [ + "PyYAML >= 6.0.3", + "prettytable >= 3.17.0", + "click >= 8.3.1", + "packaging >= 26.0", + "pendulum >= 3.2.0", +] + +[project.urls] +Source = "https://github.com/WizardBit/xjs/" + +[tool.setuptools.packages.find] +include = ["xjs*"] +exclude = ["snap*"] + +[project.scripts] +xjs = "xjs.cli:main" diff --git a/setup.py b/setup.py deleted file mode 100755 index 4bf8fd3..0000000 --- a/setup.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - -import setuptools - -# requirements = [ -# 'PyYAML>=3.13', -# 'prettytable>=0.7.2', -# 'click>=7.0', -# 'packaging>=19.0', -# 'requests>=2.21.0', -# 'pendulum>=2.0.4' -# ] - -setuptools.setup( - name="xjs", - version="0.1", - scripts=[ - "xjs", - "application.py", - "basicmachine.py", - "basicunit.py", - "colors.py", - "container.py", - "controller.py", - "machine.py", - "model.py", - "networkinterface.py", - "relation.py", - "subordinateunit.py", - "unit.py", - ], -) diff --git a/xjs/__init__.py b/xjs/__init__.py new file mode 100644 index 0000000..ba97a28 --- /dev/null +++ b/xjs/__init__.py @@ -0,0 +1,2 @@ +# xjs - offline juju status viewer +__version__ = "0.1" diff --git a/xjs/__main__.py b/xjs/__main__.py new file mode 100644 index 0000000..130bc63 --- /dev/null +++ b/xjs/__main__.py @@ -0,0 +1,2 @@ +from .cli import main +main() diff --git a/application.py b/xjs/application.py similarity index 99% rename from application.py rename to xjs/application.py index fd86a98..90539be 100644 --- a/application.py +++ b/xjs/application.py @@ -15,9 +15,9 @@ # this program. If not, see . import re -from colors import Color +from .colors import Color import pendulum -from unit import Unit +from .unit import Unit class Application: diff --git a/basicmachine.py b/xjs/basicmachine.py similarity index 98% rename from basicmachine.py rename to xjs/basicmachine.py index 30ba29a..b9c117b 100644 --- a/basicmachine.py +++ b/xjs/basicmachine.py @@ -15,8 +15,8 @@ # this program. If not, see . import re -from colors import Color -from networkinterface import NetworkInterface +from .colors import Color +from .networkinterface import NetworkInterface import pendulum diff --git a/basicunit.py b/xjs/basicunit.py similarity index 99% rename from basicunit.py rename to xjs/basicunit.py index 3245e6c..a3cbca9 100644 --- a/basicunit.py +++ b/xjs/basicunit.py @@ -15,7 +15,7 @@ # this program. If not, see . import re -from colors import Color +from .colors import Color import pendulum diff --git a/xjs b/xjs/cli.py old mode 100755 new mode 100644 similarity index 99% rename from xjs rename to xjs/cli.py index 1684afa..730b86e --- a/xjs +++ b/xjs/cli.py @@ -18,13 +18,13 @@ import json import sys import shutil -from application import Application +from .application import Application import click -from colors import Color -from controller import Controller -from machine import Machine -from model import Model -from relation import Relation +from .colors import Color +from .controller import Controller +from .machine import Machine +from .model import Model +from .relation import Relation from prettytable import PrettyTable import yaml diff --git a/colors.py b/xjs/colors.py similarity index 100% rename from colors.py rename to xjs/colors.py diff --git a/container.py b/xjs/container.py similarity index 97% rename from container.py rename to xjs/container.py index e16c2dc..d053e8a 100644 --- a/container.py +++ b/xjs/container.py @@ -14,8 +14,8 @@ # You should have received a copy of the GNU General Public License along with # this program. If not, see . -from basicmachine import BasicMachine -from colors import Color +from .basicmachine import BasicMachine +from .colors import Color class Container(BasicMachine): diff --git a/controller.py b/xjs/controller.py similarity index 100% rename from controller.py rename to xjs/controller.py diff --git a/machine.py b/xjs/machine.py similarity index 98% rename from machine.py rename to xjs/machine.py index e934f5e..c9b371b 100644 --- a/machine.py +++ b/xjs/machine.py @@ -14,8 +14,8 @@ # You should have received a copy of the GNU General Public License along with # this program. If not, see . -from basicmachine import BasicMachine -from container import Container +from .basicmachine import BasicMachine +from .container import Container class Machine(BasicMachine): diff --git a/model.py b/xjs/model.py similarity index 99% rename from model.py rename to xjs/model.py index 1885c3c..828f66c 100644 --- a/model.py +++ b/xjs/model.py @@ -15,7 +15,7 @@ # this program. If not, see . import re -from colors import Color +from .colors import Color from packaging import version import pendulum diff --git a/networkinterface.py b/xjs/networkinterface.py similarity index 99% rename from networkinterface.py rename to xjs/networkinterface.py index 1116fa5..87124d4 100644 --- a/networkinterface.py +++ b/xjs/networkinterface.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License along with # this program. If not, see . -from colors import Color +from .colors import Color class NetworkInterface: diff --git a/relation.py b/xjs/relation.py similarity index 98% rename from relation.py rename to xjs/relation.py index 6749588..42a5244 100644 --- a/relation.py +++ b/xjs/relation.py @@ -15,7 +15,7 @@ # this program. If not, see . -from application import Application +from .application import Application class Relation: """ diff --git a/subordinateunit.py b/xjs/subordinateunit.py similarity index 98% rename from subordinateunit.py rename to xjs/subordinateunit.py index f96bbe4..487fc14 100644 --- a/subordinateunit.py +++ b/xjs/subordinateunit.py @@ -15,7 +15,7 @@ # this program. If not, see . import re -from basicunit import BasicUnit +from .basicunit import BasicUnit class SubordinateUnit(BasicUnit): diff --git a/unit.py b/xjs/unit.py similarity index 97% rename from unit.py rename to xjs/unit.py index f206f51..3420b7b 100644 --- a/unit.py +++ b/xjs/unit.py @@ -15,8 +15,8 @@ # this program. If not, see . import re -from basicunit import BasicUnit -from subordinateunit import SubordinateUnit +from .basicunit import BasicUnit +from .subordinateunit import SubordinateUnit class Unit(BasicUnit): From 3060a5b2f4be00421dfbbc90e666c491e01c2ecf Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 17:27:19 +0000 Subject: [PATCH 2/7] Fix column list mutation, rename __dict__ to to_dict, remove global state --- xjs/application.py | 9 +++++---- xjs/basicmachine.py | 9 +++++---- xjs/basicunit.py | 9 +++++---- xjs/cli.py | 34 +++++++++++++++++----------------- xjs/controller.py | 2 +- xjs/model.py | 2 +- xjs/networkinterface.py | 9 +++++---- xjs/relation.py | 9 +++++---- 8 files changed, 44 insertions(+), 39 deletions(-) diff --git a/xjs/application.py b/xjs/application.py index 90539be..5d02299 100644 --- a/xjs/application.py +++ b/xjs/application.py @@ -154,7 +154,7 @@ def __init__(self, appname, appinfo=None, model=""): unit = Unit(unitname, unitinfo, self) self.units[unitname] = unit - def __dict__(self): + def to_dict(self): return {self.name: self} def add_subordinate(self, subunit): @@ -257,11 +257,12 @@ def get_column_names( self, include_controller_name=False, include_model_name=False ): """Append the controller name and/or model name as necessary""" + fields = list(self.column_names) if include_model_name: - self.column_names.insert(0, "Model") + fields.insert(0, "Model") if include_controller_name: - self.column_names.insert(0, "Controller") - return self.column_names + fields.insert(0, "Controller") + return fields def filter_dictionary(self, dictionary, key_filter): return { diff --git a/xjs/basicmachine.py b/xjs/basicmachine.py index b9c117b..e54a444 100644 --- a/xjs/basicmachine.py +++ b/xjs/basicmachine.py @@ -124,7 +124,7 @@ def __init__(self, name, info, model): interfacename, interfaceinfo, self, model ) - def __dict__(self): + def to_dict(self): return {self.name: self} def get_jujustatus_color(self): @@ -155,8 +155,9 @@ def get_column_names( self, include_controller_name=False, include_model_name=False ): """Append the controller name and/or model name as necessary""" + fields = list(self.column_names) if include_model_name: - self.column_names.insert(0, "Model") + fields.insert(0, "Model") if include_controller_name: - self.column_names.insert(0, "Controller") - return self.column_names + fields.insert(0, "Controller") + return fields diff --git a/xjs/basicunit.py b/xjs/basicunit.py index a3cbca9..068d43b 100644 --- a/xjs/basicunit.py +++ b/xjs/basicunit.py @@ -99,7 +99,7 @@ def __init__(self, name, info, controller): if "leader" in info: self.leader = info["leader"] - def __dict__(self): + def to_dict(self): return {self.name: self} def get_workloadstatus_color(self): @@ -132,8 +132,9 @@ def get_column_names( self, include_controller_name=False, include_model_name=False ): """Append the controller name and/or model name as necessary""" + fields = list(self.column_names) if include_model_name: - self.column_names.insert(0, "Model") + fields.insert(0, "Model") if include_controller_name: - self.column_names.insert(0, "Controller") - return self.column_names + fields.insert(0, "Controller") + return fields diff --git a/xjs/cli.py b/xjs/cli.py index 730b86e..875469e 100644 --- a/xjs/cli.py +++ b/xjs/cli.py @@ -28,10 +28,8 @@ from prettytable import PrettyTable import yaml -controllers = {} - -def load_status_file(inputfile): +def load_status_file(inputfile, controllers): """Load a juju status file, inputfile is a yaml or json file""" rawstatus = {} @@ -102,7 +100,7 @@ def load_status_file(inputfile): model.add_relation(relation) -def console_print_model_info(color=True): +def console_print_model_info(controllers, color=True): """Filter and sort model info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -114,7 +112,7 @@ def console_print_model_info(color=True): console_print_object(print_what=models, color=color) -def console_print_application_info(color=True, hide_scale_zero=False): +def console_print_application_info(controllers, color=True, hide_scale_zero=False): """Filter and sort application info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -144,7 +142,7 @@ def console_print_application_info(color=True, hide_scale_zero=False): ) -def console_print_unit_info(color=True, hide_subordinate_units=False): +def console_print_unit_info(controllers, color=True, hide_subordinate_units=False): """Filter and sort unit info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -182,7 +180,7 @@ def console_print_unit_info(color=True, hide_subordinate_units=False): ) -def console_print_networkinterface_info(color=True, include_containers=True): +def console_print_networkinterface_info(controllers, color=True, include_containers=True): """Filter and sort network info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -216,7 +214,7 @@ def console_print_networkinterface_info(color=True, include_containers=True): ) -def console_print_machine_info(color=True, include_containers=True): +def console_print_machine_info(controllers, color=True, include_containers=True): """Filter and sort machine info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -245,7 +243,7 @@ def console_print_machine_info(color=True, include_containers=True): ) -def console_print_relations(color=True): +def console_print_relations(controllers, color=True): """Filter and sort relation info to print in a table here""" relations = [] include_controller_name = False @@ -310,6 +308,7 @@ def filter_dictionary(dictionary, key_filter): def filter_results( + controllers, ctrl_filter="", model_filter="", app_filter="", @@ -318,7 +317,6 @@ def filter_results( machine_filter="", ): """Filter the status""" - global controllers filtered_controllers = {} # Filter the Controllers @@ -544,8 +542,9 @@ def main( """ color = not no_color + controllers = {} for statusfile in statusfiles: - load_status_file(statusfile) + load_status_file(statusfile, controllers) # If no particular field was specified, show them all if ( @@ -574,6 +573,7 @@ def main( or subordinate != "" ): filter_results( + controllers, ctrl_filter=controller, model_filter=model, app_filter=application, @@ -583,22 +583,22 @@ def main( ) if show_model: - console_print_model_info(color) + console_print_model_info(controllers, color) print("") if show_apps: - console_print_application_info(color, hide_scale_zero) + console_print_application_info(controllers, color, hide_scale_zero) print("") if show_units: - console_print_unit_info(color, hide_subordinate_units) + console_print_unit_info(controllers, color, hide_subordinate_units) print("") if show_machines: - console_print_machine_info(color, include_containers) + console_print_machine_info(controllers, color, include_containers) print("") if show_net: - console_print_networkinterface_info(color, include_containers) + console_print_networkinterface_info(controllers, color, include_containers) print("") if show_relations: - console_print_relations(color) + console_print_relations(controllers, color) print("") diff --git a/xjs/controller.py b/xjs/controller.py index 5a7f72b..ee6cc13 100644 --- a/xjs/controller.py +++ b/xjs/controller.py @@ -61,7 +61,7 @@ def __init__(self, controllername, controllerinfo={}): "DD MMM YYYY HH:mm:ss", ) - def __dict__(self): + def to_dict(self): return {self.name: self} def update_timestamp(self, date): diff --git a/xjs/model.py b/xjs/model.py index 828f66c..fd583b7 100644 --- a/xjs/model.py +++ b/xjs/model.py @@ -105,7 +105,7 @@ def __init__(self, modelinfo, controller, juju1env=None): self.upgradeavailable = modelinfo["upgrade-available"] self.notes.append("upgrade available: " + self.upgradeavailable) - def __dict__(self): + def to_dict(self): return {self.name: self} def add_application(self, application): diff --git a/xjs/networkinterface.py b/xjs/networkinterface.py index 87124d4..47d77f6 100644 --- a/xjs/networkinterface.py +++ b/xjs/networkinterface.py @@ -53,7 +53,7 @@ def __init__(self, interfacename, interfaceinfo, parent, model): if "gateway" in interfaceinfo: self.gateway = interfaceinfo["gateway"] - def __dict__(self): + def to_dict(self): return {self.name: self} def get_isup_color(self): @@ -103,8 +103,9 @@ def get_column_names( self, include_controller_name=False, include_model_name=False ): """Append the controller name and/or model name as necessary""" + fields = list(self.column_names) if include_model_name: - self.column_names.insert(0, "Model") + fields.insert(0, "Model") if include_controller_name: - self.column_names.insert(0, "Controller") - return self.column_names + fields.insert(0, "Controller") + return fields diff --git a/xjs/relation.py b/xjs/relation.py index 42a5244..10b99f9 100644 --- a/xjs/relation.py +++ b/xjs/relation.py @@ -44,7 +44,7 @@ def __init__(self, model, name, partnername, applicationname): else: self.partner = Application(partnername) - def __dict__(self): + def to_dict(self): return {self.name: self} def get_row( @@ -65,8 +65,9 @@ def get_column_names( self, include_controller_name=False, include_model_name=False ): """Append the controller name and/or model name as necessary""" + fields = list(self.column_names) if include_model_name: - self.column_names.insert(0, "Model") + fields.insert(0, "Model") if include_controller_name: - self.column_names.insert(0, "Controller") - return self.column_names + fields.insert(0, "Controller") + return fields From 02dd6925a46cfdcb3c02a415e28ce351fc2182eb Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 17:27:30 +0000 Subject: [PATCH 3/7] Add *.snap --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 82adb58..17f48fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ __pycache__ venv +*.snap From 5271e6d6e33818908402852058e1b442e6f06684 Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 17:45:24 +0000 Subject: [PATCH 4/7] Create License file --- LICENSE | 14 ++++++++++++++ my.md | 15 +++++++++++++++ xjs/application.py | 18 ++---------------- xjs/basicmachine.py | 16 +--------------- xjs/basicunit.py | 16 +--------------- xjs/cli.py | 21 +++------------------ xjs/colors.py | 16 +--------------- xjs/container.py | 16 +--------------- xjs/controller.py | 18 +----------------- xjs/machine.py | 16 +--------------- xjs/model.py | 15 +-------------- xjs/networkinterface.py | 16 +--------------- xjs/relation.py | 16 +--------------- xjs/subordinateunit.py | 16 +--------------- xjs/unit.py | 16 +--------------- 15 files changed, 45 insertions(+), 200 deletions(-) create mode 100644 LICENSE create mode 100644 my.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e4fefe0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +xjs - offline juju status viewer +Copyright 2019 Canonical Ltd. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/my.md b/my.md new file mode 100644 index 0000000..4ec1b3c --- /dev/null +++ b/my.md @@ -0,0 +1,15 @@ +# XJS Codebase Analysis + +## ๐Ÿ› Bugs & Defects + +## ๐Ÿงน Code Quality & Maintainability + +## ๐Ÿ— Architecture & Design + +## โšก Performance + +## โœจ Missing Features + +## ๐Ÿ”ง CI/CD & Tooling + +## ๐Ÿ“ Minor / Cosmetic diff --git a/xjs/application.py b/xjs/application.py index 5d02299..4cac5f3 100644 --- a/xjs/application.py +++ b/xjs/application.py @@ -1,25 +1,11 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .colors import Color import pendulum from .unit import Unit - class Application: column_names = [ "App", @@ -208,7 +194,7 @@ def get_charmorigin_color(self): """ Return a charm origin string with correct colors based on the origin """ - if self.charmorigin != "jujucharms": + if self.charmorigin != "charmhub": return Color.Fg.Yellow + self.charmorigin + Color.Reset else: return self.charmorigin diff --git a/xjs/basicmachine.py b/xjs/basicmachine.py index e54a444..2aeac01 100644 --- a/xjs/basicmachine.py +++ b/xjs/basicmachine.py @@ -1,25 +1,11 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .colors import Color from .networkinterface import NetworkInterface import pendulum - class BasicMachine: """ A BasicMachine Object is inherited by Machines and Containers so common diff --git a/xjs/basicunit.py b/xjs/basicunit.py index 068d43b..2b32eba 100644 --- a/xjs/basicunit.py +++ b/xjs/basicunit.py @@ -1,24 +1,10 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .colors import Color import pendulum - class BasicUnit: """ A BasicMachine Object is inherited by Units and Subordinates so common diff --git a/xjs/cli.py b/xjs/cli.py index 875469e..5f3fd26 100644 --- a/xjs/cli.py +++ b/xjs/cli.py @@ -1,19 +1,5 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - +# SPDX-License-Identifier: GPL-3.0-only import json import sys @@ -28,7 +14,6 @@ from prettytable import PrettyTable import yaml - def load_status_file(inputfile, controllers): """Load a juju status file, inputfile is a yaml or json file""" rawstatus = {} @@ -93,9 +78,9 @@ def load_status_file(inputfile, controllers): for appname, appinfo in rawstatus[applicationkey].items(): if "relations" in appinfo: for relationname, partnerapps in appinfo["relations"].items(): - for parnerapp in partnerapps: + for partnerapp in partnerapps: relation = Relation( - model, relationname, parnerapp, appname + model, relationname, partnerapp, appname ) model.add_relation(relation) diff --git a/xjs/colors.py b/xjs/colors.py index 8df72b4..70ee535 100644 --- a/xjs/colors.py +++ b/xjs/colors.py @@ -1,19 +1,5 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - +# SPDX-License-Identifier: GPL-3.0-only # TODO color logic needs to be adjusted, this should handle colors for more # than just the console diff --git a/xjs/container.py b/xjs/container.py index d053e8a..fa1ad9f 100644 --- a/xjs/container.py +++ b/xjs/container.py @@ -1,23 +1,9 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only from .basicmachine import BasicMachine from .colors import Color - class Container(BasicMachine): iscontainer = True diff --git a/xjs/controller.py b/xjs/controller.py index ee6cc13..787f35e 100644 --- a/xjs/controller.py +++ b/xjs/controller.py @@ -1,24 +1,8 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - +# SPDX-License-Identifier: GPL-3.0-only import re import pendulum -import requests - class Controller: zerodate = pendulum.from_format("0", "x", tz="UTC") diff --git a/xjs/machine.py b/xjs/machine.py index c9b371b..2ec5ea6 100644 --- a/xjs/machine.py +++ b/xjs/machine.py @@ -1,23 +1,9 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only from .basicmachine import BasicMachine from .container import Container - class Machine(BasicMachine): iscontainer = False diff --git a/xjs/model.py b/xjs/model.py index fd583b7..49244c5 100644 --- a/xjs/model.py +++ b/xjs/model.py @@ -1,18 +1,5 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .colors import Color diff --git a/xjs/networkinterface.py b/xjs/networkinterface.py index 47d77f6..bfb1609 100644 --- a/xjs/networkinterface.py +++ b/xjs/networkinterface.py @@ -1,22 +1,8 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only from .colors import Color - class NetworkInterface: column_names = [ "Machine", diff --git a/xjs/relation.py b/xjs/relation.py index 10b99f9..75ac31e 100644 --- a/xjs/relation.py +++ b/xjs/relation.py @@ -1,19 +1,5 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . - +# SPDX-License-Identifier: GPL-3.0-only from .application import Application diff --git a/xjs/subordinateunit.py b/xjs/subordinateunit.py index 487fc14..2d2e70a 100644 --- a/xjs/subordinateunit.py +++ b/xjs/subordinateunit.py @@ -1,23 +1,9 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .basicunit import BasicUnit - class SubordinateUnit(BasicUnit): issubordinate = True diff --git a/xjs/unit.py b/xjs/unit.py index 3420b7b..3c1dbbc 100644 --- a/xjs/unit.py +++ b/xjs/unit.py @@ -1,24 +1,10 @@ #!/usr/bin/env python3 -# This file is part of xjs a tool used to disply offline juju status -# Copyright 2019 Canonical Ltd. -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License version 3, as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program. If not, see . +# SPDX-License-Identifier: GPL-3.0-only import re from .basicunit import BasicUnit from .subordinateunit import SubordinateUnit - class Unit(BasicUnit): issubordinate = False From 21ee8295b5b60cdc2fbcd10c940806f2c8b53b91 Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 17:58:12 +0000 Subject: [PATCH 5/7] Minor Fixes --- README.md | 2 ++ requirements.txt | 1 - snapit | 6 +++--- xjs/basicmachine.py | 2 +- xjs/cli.py | 14 +++----------- xjs/model.py | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 085a542..b889a8c 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,11 @@ Options: -d, --show-model Show model information -n, --show-net Show network interface information -u, --show-units Show unit information + -r, --show-relations Show relation information --subordinate Show only the subordinate unit with the specified name --unit Show only the unit with the specified name + --version Show the version and exit. --help Show this message and exit. ``` diff --git a/requirements.txt b/requirements.txt index 07cbe2c..57035b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ PyYAML >= 6.0.3 prettytable >= 3.17.0 click >= 8.3.1 packaging >= 26.0 -requests >= 2.32.5 pendulum >= 3.2.0 diff --git a/snapit b/snapit index cca96c8..e1645f0 100755 --- a/snapit +++ b/snapit @@ -22,9 +22,9 @@ snapcraft --debug snapcraft clean # Install with: -# sudo snap install supctl_0.1_amd64.snap --devmode --dangerous +# sudo snap install xjs_0.1_amd64.snap --devmode --dangerous # OLD CMDS # source testit -# snapcraft push --release=beta ./supctl_beta2_amd64.snap -# ln supctl_beta2_amd64.snap ~/sp.snap +# snapcraft push --release=beta ./xjs_beta2_amd64.snap +# ln xjs_beta2_amd64.snap ~/sp.snap diff --git a/xjs/basicmachine.py b/xjs/basicmachine.py index 2aeac01..060a684 100644 --- a/xjs/basicmachine.py +++ b/xjs/basicmachine.py @@ -51,7 +51,7 @@ def __init__(self, name, info, model): self.dnsname = info["dns-name"] else: self.dnsname = "PENDING" - if "ipaddresses" in info: + if "ip-addresses" in info: self.ipaddresses = info["ip-addresses"] else: self.ipaddresses = "NA" diff --git a/xjs/cli.py b/xjs/cli.py index 5f3fd26..f6f594a 100644 --- a/xjs/cli.py +++ b/xjs/cli.py @@ -6,6 +6,7 @@ import shutil from .application import Application import click +from . import __version__ from .colors import Color from .controller import Controller from .machine import Machine @@ -114,9 +115,7 @@ def console_print_application_info(controllers, color=True, hide_scale_zero=Fals include_model_name = True for modelname, model in controller.models.items(): for appname, app in model.applications.items(): - if hide_scale_zero and app.get_scale() > 0: - apps.append(app) - elif not hide_scale_zero: + if not hide_scale_zero or app.get_scale() > 0: apps.append(app) if len(apps) > 0: console_print_object( @@ -148,14 +147,6 @@ def console_print_unit_info(controllers, color=True, hide_subordinate_units=Fals if not hide_subordinate_units: for subunitname, subunit in unit.subordinates.items(): units.append(subunit) - # Bad logic, if an application has 0 units it might only be - # a subordinate app and not filtered - # if not hide_subordinate_units and len(application.units) == 0: - # for ( - # subunitname, - # subunit, - # ) in application.subordinates.items(): - # units.append(subunit) if len(units) > 0: console_print_object( print_what=units, @@ -391,6 +382,7 @@ def filter_results( controllers = filtered_controllers +@click.version_option(__version__) @click.command() @click.option( "--application", diff --git a/xjs/model.py b/xjs/model.py index 49244c5..68ba852 100644 --- a/xjs/model.py +++ b/xjs/model.py @@ -108,8 +108,8 @@ def add_container(self, container): self.containers[container.name] = container def add_relation(self, relation): + """Add a relation if it doesn't already exist""" if relation is not None: - """Add a relation if it doesn't already exist""" if not relation.name in self.relations: self.relations[relation.name] = [] self.relations[relation.name].append(relation) From b70eb5f5fbcefdfd387bcf504819c198d4cdacc2 Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 18:26:59 +0000 Subject: [PATCH 6/7] Fix syntax --- xjs/application.py | 10 ++++++---- xjs/basicmachine.py | 13 +++++++++---- xjs/basicunit.py | 9 +++++++-- xjs/cli.py | 21 ++++++++++++++++----- xjs/container.py | 1 + xjs/controller.py | 9 +++++++-- xjs/machine.py | 1 + xjs/model.py | 29 ++++++++++++++++++++--------- xjs/networkinterface.py | 1 + xjs/relation.py | 1 + xjs/subordinateunit.py | 3 ++- xjs/unit.py | 13 ++++++++++--- 12 files changed, 81 insertions(+), 30 deletions(-) diff --git a/xjs/application.py b/xjs/application.py index 4cac5f3..1f23f32 100644 --- a/xjs/application.py +++ b/xjs/application.py @@ -6,6 +6,7 @@ import pendulum from .unit import Unit + class Application: column_names = [ "App", @@ -27,7 +28,7 @@ def __init__(self, appname, appinfo=None, model=""): object from a juju status output """ appinfo = appinfo if isinstance(appinfo, dict) else {} - + # Default Values self.notes = [] self.units = {} @@ -46,11 +47,10 @@ def __init__(self, appname, appinfo=None, model=""): base = appinfo.get("base") if isinstance(base, dict): - self.base = f"{base.get('name','')}@{base.get('channel','')}" + self.base = f"{base.get('name', '')}@{base.get('channel', '')}" else: self.base = base or "NA" - if "os" in appinfo: self.os = appinfo["os"] else: @@ -90,7 +90,9 @@ def __init__(self, appname, appinfo=None, model=""): if statuskey in appinfo and "since" in appinfo[statuskey]: if re.match(r".*Z$", appinfo[statuskey]["since"]): - appinfo[statuskey]["since"] = re.sub(r"Z$", "", appinfo[statuskey]["since"]) + appinfo[statuskey]["since"] = re.sub( + r"Z$", "", appinfo[statuskey]["since"] + ) self.since = pendulum.from_format( appinfo[statuskey]["since"], "DD MMM YYYY HH:mm:ss", diff --git a/xjs/basicmachine.py b/xjs/basicmachine.py index 060a684..d71abf6 100644 --- a/xjs/basicmachine.py +++ b/xjs/basicmachine.py @@ -6,6 +6,7 @@ from .networkinterface import NetworkInterface import pendulum + class BasicMachine: """ A BasicMachine Object is inherited by Machines and Containers so common @@ -65,10 +66,10 @@ def __init__(self, name, info, model): else: self.machinestatus = "NA" self.machinemessage = "" - + base = info.get("base") if isinstance(base, dict): - self.base = f"{base.get('name','')}@{base.get('channel','')}" + self.base = f"{base.get('name', '')}@{base.get('channel', '')}" else: self.base = base if base else "NA" self.model = model @@ -76,7 +77,9 @@ def __init__(self, name, info, model): # Required Dates if "juju-status" in info: if re.match(r".*Z$", info["juju-status"]["since"]): - info["juju-status"]["since"] = re.sub(r"Z$", "", info["juju-status"]["since"]) + info["juju-status"]["since"] = re.sub( + r"Z$", "", info["juju-status"]["since"] + ) self.jujusince = pendulum.from_format( info["juju-status"]["since"], "DD MMM YYYY HH:mm:ss", @@ -89,7 +92,9 @@ def __init__(self, name, info, model): model.controller.update_timestamp(self.jujusince) if "machine-status" in info: if re.match(r".*Z$", info["machine-status"]["since"]): - info["machine-status"]["since"] = re.sub(r"Z$", "", info["machine-status"]["since"]) + info["machine-status"]["since"] = re.sub( + r"Z$", "", info["machine-status"]["since"] + ) self.machinesince = pendulum.from_format( info["machine-status"]["since"], "DD MMM YYYY HH:mm:ss", diff --git a/xjs/basicunit.py b/xjs/basicunit.py index 2b32eba..84851bc 100644 --- a/xjs/basicunit.py +++ b/xjs/basicunit.py @@ -5,6 +5,7 @@ from .colors import Color import pendulum + class BasicUnit: """ A BasicMachine Object is inherited by Units and Subordinates so common @@ -55,7 +56,9 @@ def __init__(self, name, info, controller): # Required Dates if re.match(r".*Z$", info["workload-status"]["since"]): - info["workload-status"]["since"] = re.sub(r"Z$", "", info["workload-status"]["since"]) + info["workload-status"]["since"] = re.sub( + r"Z$", "", info["workload-status"]["since"] + ) self.workloadsince = pendulum.from_format( info["workload-status"]["since"], "DD MMM YYYY HH:mm:ss", @@ -67,7 +70,9 @@ def __init__(self, name, info, controller): ) controller.update_timestamp(self.workloadsince) if re.match(r".*Z$", info[statuskey]["since"]): - info[statuskey]["since"] = re.sub(r"Z$", "", info[statuskey]["since"]) + info[statuskey]["since"] = re.sub( + r"Z$", "", info[statuskey]["since"] + ) self.jujusince = pendulum.from_format( info[statuskey]["since"], "DD MMM YYYY HH:mm:ss", tz="UTC" ) diff --git a/xjs/cli.py b/xjs/cli.py index f6f594a..7bc297a 100644 --- a/xjs/cli.py +++ b/xjs/cli.py @@ -15,6 +15,7 @@ from prettytable import PrettyTable import yaml + def load_status_file(inputfile, controllers): """Load a juju status file, inputfile is a yaml or json file""" rawstatus = {} @@ -98,7 +99,9 @@ def console_print_model_info(controllers, color=True): console_print_object(print_what=models, color=color) -def console_print_application_info(controllers, color=True, hide_scale_zero=False): +def console_print_application_info( + controllers, color=True, hide_scale_zero=False +): """Filter and sort application info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -126,7 +129,9 @@ def console_print_application_info(controllers, color=True, hide_scale_zero=Fals ) -def console_print_unit_info(controllers, color=True, hide_subordinate_units=False): +def console_print_unit_info( + controllers, color=True, hide_subordinate_units=False +): """Filter and sort unit info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -156,7 +161,9 @@ def console_print_unit_info(controllers, color=True, hide_subordinate_units=Fals ) -def console_print_networkinterface_info(controllers, color=True, include_containers=True): +def console_print_networkinterface_info( + controllers, color=True, include_containers=True +): """Filter and sort network info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -190,7 +197,9 @@ def console_print_networkinterface_info(controllers, color=True, include_contain ) -def console_print_machine_info(controllers, color=True, include_containers=True): +def console_print_machine_info( + controllers, color=True, include_containers=True +): """Filter and sort machine info to print in a table here""" # TODO Handle Sort # TODO Handle Filter @@ -572,7 +581,9 @@ def main( console_print_machine_info(controllers, color, include_containers) print("") if show_net: - console_print_networkinterface_info(controllers, color, include_containers) + console_print_networkinterface_info( + controllers, color, include_containers + ) print("") if show_relations: console_print_relations(controllers, color) diff --git a/xjs/container.py b/xjs/container.py index fa1ad9f..2d885e2 100644 --- a/xjs/container.py +++ b/xjs/container.py @@ -4,6 +4,7 @@ from .basicmachine import BasicMachine from .colors import Color + class Container(BasicMachine): iscontainer = True diff --git a/xjs/controller.py b/xjs/controller.py index 787f35e..0ea53b4 100644 --- a/xjs/controller.py +++ b/xjs/controller.py @@ -4,6 +4,7 @@ import re import pendulum + class Controller: zerodate = pendulum.from_format("0", "x", tz="UTC") @@ -25,9 +26,13 @@ def __init__(self, controllername, controllerinfo={}): if "timestamp" in controllerinfo: self.timestampprovided = True if re.match(r"^\d\d:\d\d:\d\d", controllerinfo["timestamp"]): - controllerinfo["timestamp"] = "01 Jan 1970 " + controllerinfo["timestamp"] + controllerinfo["timestamp"] = ( + "01 Jan 1970 " + controllerinfo["timestamp"] + ) if re.match(r".*Z$", controllerinfo["timestamp"]): - controllerinfo["timestamp"] = re.sub(r"Z$", "", controllerinfo["timestamp"]) + controllerinfo["timestamp"] = re.sub( + r"Z$", "", controllerinfo["timestamp"] + ) self.timestamp = pendulum.from_format( controllerinfo["timestamp"], "DD MMM YYYY HH:mm:ss", diff --git a/xjs/machine.py b/xjs/machine.py index 2ec5ea6..564e062 100644 --- a/xjs/machine.py +++ b/xjs/machine.py @@ -4,6 +4,7 @@ from .basicmachine import BasicMachine from .container import Container + class Machine(BasicMachine): iscontainer = False diff --git a/xjs/model.py b/xjs/model.py index 68ba852..e55185f 100644 --- a/xjs/model.py +++ b/xjs/model.py @@ -6,6 +6,7 @@ from packaging import version import pendulum + class Model: # TODO get latest juju version dynamically latest_juju_version = version.parse("3.6.20") @@ -73,9 +74,13 @@ def __init__(self, modelinfo, controller, juju1env=None): self.sla = "NA" # Required Dates - if "model-status" in modelinfo and "since" in modelinfo["model-status"]: + if "model-status" in modelinfo and "since" in modelinfo[ + "model-status" + ]: if re.match(r".*Z$", modelinfo["model-status"]["since"]): - modelinfo["model-status"]["since"] = re.sub(r"Z$", "", modelinfo["model-status"]["since"]) + modelinfo["model-status"]["since"] = re.sub( + r"Z$", "", modelinfo["model-status"]["since"] + ) self.since = pendulum.from_format( modelinfo["model-status"]["since"], "DD MMM YYYY HH:mm:ss", @@ -110,22 +115,28 @@ def add_container(self, container): def add_relation(self, relation): """Add a relation if it doesn't already exist""" if relation is not None: - if not relation.name in self.relations: + if relation.name not in self.relations: self.relations[relation.name] = [] self.relations[relation.name].append(relation) return else: - if not self.get_relation(relation.name, relation.application.name, - relation.partner.name): + if not self.get_relation( + relation.name, relation.application.name, + relation.partner.name, + ): self.relations[relation.name].append(relation) def get_relation(self, name, app_name, partner_name): if name in self.relations: for relation in self.relations[name]: - if ((relation.application.name == app_name - and relation.partner.name == partner_name) or - (relation.partner.name == app_name and - relation.application.name == partner_name)): + if ( + (relation.application.name == app_name + and relation.partner.name == partner_name) + or ( + relation.partner.name == app_name + and relation.application.name == partner_name + ) + ): return relation return None else: diff --git a/xjs/networkinterface.py b/xjs/networkinterface.py index bfb1609..5ddf365 100644 --- a/xjs/networkinterface.py +++ b/xjs/networkinterface.py @@ -3,6 +3,7 @@ from .colors import Color + class NetworkInterface: column_names = [ "Machine", diff --git a/xjs/relation.py b/xjs/relation.py index 75ac31e..b8fc2ae 100644 --- a/xjs/relation.py +++ b/xjs/relation.py @@ -3,6 +3,7 @@ from .application import Application + class Relation: """ A Relation is a juju relation between 2 juju applications, juju status diff --git a/xjs/subordinateunit.py b/xjs/subordinateunit.py index 2d2e70a..06d3fd4 100644 --- a/xjs/subordinateunit.py +++ b/xjs/subordinateunit.py @@ -4,6 +4,7 @@ import re from .basicunit import BasicUnit + class SubordinateUnit(BasicUnit): issubordinate = True @@ -20,7 +21,7 @@ def __init__(self, subunitname, subunitinfo, unit): # Required Variables self.unit = unit # Not sure if required anymore but causes error - #self.upgradingfrom = subunitinfo["upgrading-from"] + # self.upgradingfrom = subunitinfo["upgrading-from"] self.machine = unit.machine def create_application_relation(self): diff --git a/xjs/unit.py b/xjs/unit.py index 3c1dbbc..288d07c 100644 --- a/xjs/unit.py +++ b/xjs/unit.py @@ -5,6 +5,7 @@ from .basicunit import BasicUnit from .subordinateunit import SubordinateUnit + class Unit(BasicUnit): issubordinate = False @@ -21,11 +22,17 @@ def __init__(self, unitname, unitinfo, application): # Required Variables self.application = application if "machine" in unitinfo: - match = re.match(r"(\d+)\/(lx[cd]|kvm)\/(\d+)$", unitinfo["machine"]) + match = re.match( + r"(\d+)\/(lx[cd]|kvm)\/(\d+)$", unitinfo["machine"] + ) if match: - self.machine = application.model.get_container(unitinfo["machine"]) + self.machine = application.model.get_container( + unitinfo["machine"] + ) else: - self.machine = application.model.get_machine(unitinfo["machine"]) + self.machine = application.model.get_machine( + unitinfo["machine"] + ) else: self.machine = None From 9211551550c8b9575cc03fa65d9432ce8b0d0914 Mon Sep 17 00:00:00 2001 From: Brandon Castillo Date: Fri, 29 May 2026 20:45:53 +0000 Subject: [PATCH 7/7] Fix machine filtering --- xjs/cli.py | 16 ++++++++++++++++ xjs/model.py | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/xjs/cli.py b/xjs/cli.py index 7bc297a..5f5da58 100644 --- a/xjs/cli.py +++ b/xjs/cli.py @@ -388,6 +388,22 @@ def filter_results( for controllername in empty_controllers: del filtered_controllers[controllername] + # Filter the Machines + if machine_filter != "": + empty_controllers = [] + for controllername, controller in filtered_controllers.items(): + empty_models = [] + for modelname, model in controller.models.items(): + model.filter_machines(machine_filter) + if len(model.machines) == 0: + empty_models.append(modelname) + for modelname in empty_models: + del controller.models[modelname] + if len(controller.models) == 0: + empty_controllers.append(controllername) + for controllername in empty_controllers: + del filtered_controllers[controllername] + controllers = filtered_controllers diff --git a/xjs/model.py b/xjs/model.py index e55185f..0840d74 100644 --- a/xjs/model.py +++ b/xjs/model.py @@ -246,6 +246,16 @@ def filter_applications(self, app_filter): self.applications = {**apps, **parent_apps} self.reset_machines() + def filter_machines(self, machine_filter): + self.machines = { + key: value + for (key, value) in self.machines.items() + if key == machine_filter + } + self.containers = {} + for machine in self.machines.values(): + self.containers.update(machine.containers) + def reset_machines(self): machines = {} containers = {}