-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance charm's life-cycle #118
Conversation
for the exporter service.
linting. Co-authored-by: jneo8 <[email protected]>
Unit test / functional test will be added if the approach is approved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some question need more information and discussion.
src/charm.py
Outdated
resource_white_list, | ||
resource_black_list=self._stored.resource_install_status, # type: ignore[arg-type] | ||
) | ||
self._stored.resource_install_status = resource_install_status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
status
make my confuse with MaintenanceStatus
One is boolean, another is juju's unit status.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe result
might be a better choice -- resource_install_result
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, I personally will never think of status
with *Status
(in libjuju) 😆
src/exporter.py
Outdated
@@ -0,0 +1,137 @@ | |||
"""Exporter relation observer.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do like the idea we try to let exporter service follow the charm's life cycle. I would like to say it's a nice design. So I am +1 with this.
But in the other hand I would like to point out because we create one more layer between the service and the charm, it somehow increase the complexity we have here.
Is that possible we combine the exporter and service together? Or you think it has benefit we have this three layers design?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your point. Introducing another layer for the charm to interface with the exporter functionality might be quite confusing.
So I think it's good to think about what's the core idea behind the src/exporter.py::Observer
and src/service.py::Exporter
and see if there's a chance we can combine or if we really need those 2 layers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear, there is no "3 layers for the charm". Each subclass of ops.Object
is sort of like a leaf to the ops.CharmBase
, so there is only 2 layers.
If we choose to combine the Service(object)
and the relation handler Observer(ops.Object)
together, then the service class will be like Service(ops.Object)
, then it's "inheritance", and I used to do that. But there's no clear "inheritance" between a relation handler and a exporter service. So I chose composition over inheritance. However, that's only a benefit on the meaning of code.
The other benefits are:
- Easier unit test: relation handler is decoupled with whatever service (systemd or snap), so the test for relation handler should be unchanged even if we later switch from a systemd to a snap.
- Easier to rewrite the exporter from a systemd service to a snap, since they are decoupled.
But that's being said, the benefit is in the future, and not present. If we want to refactor it later, we can still do it later (but I doubt we will do it later)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I say three layers it's because when we try to debug we still need to check three classes. charm
-> exporter
-> service
. Yes, we have a composition over inheritance pattern here but it somehow make it more hard to debugging. For decoupled
part because we already extract the service from charm, the service is already decoupled from charm. But do we need to decouple service
from exporter
life cycle now?
The different of one service
vs service
+ exporter
is when we try to switch from systemd to snap:
service
: Re-implement theservice
.service
+exporter
: Re-implement theservice
, but can we ignore the life cycle define inexporter
in reality?
Also when the time coming, do we need to keep the systemd service class?
If you think the both answers are yes, then we can keep this design else I suggest to have merge them use mixin.
src/charm.py
Outdated
if not all(resource_install_status.values()): | ||
failed_resources = [r for r, s in resource_install_status.items() if not s] | ||
msg = f"Failed to install resources: {', '.join(failed_resources)}" | ||
logger.error(msg) | ||
self._stored.blocked_msg = msg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocked_msg
seems not useful anymore.
src/charm.py
Outdated
self.hw_tool_helper.remove(self.model.resources) | ||
self.exporter.uninstall() | ||
self._stored.agent_installed = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having manual config here. Maybe a functional to set default value?
self.model.unit.status = ErrorStatus(msg) | ||
return | ||
|
||
self.update_status() | ||
|
||
def _on_remove(self, _: EventBase) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happen if remove fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, there's not remove fail logic in the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it just get into error state?
src/charm.py
Outdated
], | ||
) | ||
self.exporter = Exporter(self.charm_dir) | ||
self.exporter_observer = exporter.Observer(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.exporter_observer
sounds not precise. Just self.exporter
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I want to think about what we're "observing" here. exporter.Observer
feels like I'm initializing a class that observes the exporter but it looks more like the class is observing the cos-agent
relation?
src/charm.py
Outdated
success, message = self.exporter_observer.validate_agent_configs(options) | ||
if not success: | ||
self.model.unit.status = BlockedStatus(message) | ||
event.defer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the config is not valid. User need to re-config it, right?
Then why we need this event.defer()
return True | ||
|
||
|
||
def validate_checksum(support_version_infos: t.List[ToolVersionInfo], path: Path) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's very weird that I have a checksum
module and the checksum function is here. I suggest to move it back to checksum module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could argue that checksum.py
contains all the checksums for the various resource tools but doesn't need to contain the actual validation logic which could happen elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checksum.py
is only the definition of the checksum similar to config.py
. The main purpose of validating checksum is for the resource, so I put them together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can maybe move checksum.py
to config.py
, and rename config.py
to constants.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main purpose of validating checksum is for the resource, so I put them together.
I agree with this approach. We could possibly rename checksum.py
to maybe tool_info.py
since it contains more than the checksums for each of the tools. The checksum validation functionality could go into the ResourceHelper
class I detailed in my other comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will still choose to have all the part relate to checksum
in this module. I don't want to switch between charm to verify the logic of checksum checks.
constants.py
is somehow bad because we are not just putting constants in the config. All the related config or loading functions will be here also. No necessary to split it.
One more thing to add is we would like to have someone easily contribute to the checksum. So intuitively checksum.py
is the best place.
Sorry, still -1 for this change.
@@ -0,0 +1,101 @@ | |||
"""Wrapper for `model.Resources`.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The definition of hardware
, hw_tool
, and hw_resources
modules is somehow overlapping. I suggest to merge hardware
and hw_resource
together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are indeed 2 different distinct items + 1 helper: hardware (for hardware verifier), resources (for fetching user uploaded resource) + hw_tool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are my thoughts:
current
hw_resources.py
- contains exception classes for resource check fails
- utils related to resource file handling eg:
check_file_exists
,validate_size
etc.
hardware.py
- constant containing supported raid storages
- utils for general hw checks. eg:
lshw
,get_bmc_address
- utils for hw verification,
{raid,bmc}_hw_verifier}
hw_tools.py
- utils related to package management, eg:
install_deb
,remove_deb
,check_deb_pkg_installed
- strategy classes for various tools
- HwToolHelper class
what i'm proposing
utils.py/ helpers.py
- This will be a general class with util methods contained in these helper classes similar to
HwToolHelper
ResourceHelper
: This contains mostly the helper methods fromhw_resources.py
--check_file_exists
,validate_size
etc.PackageHelper
-- will contain all theapt
anddeb
related functions --install_deb
,remove_deb
,check_deb_pkg_installed
,install_apt_package
HwHelper
: contains helper methods for hardware related tasks. --lshw
,get_bmc_address
,validate_redfish_credentials
HwVerifier
: contains the "verifier" methods fromhardware.py
and generate the whitelist --{raid,bmc}_hw_verifier}
- Also exception classes for resource checks.
hw_tools.py
-
This will now only have the strategy classes +
HwToolHelper
class -
The constant containing supported raid storages from
hardware.py
should go intoconfig.py
/constants.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest just keep it simple:
hw_helpers.py
for all the operation not relate to resource directly, including check file size or build soft-link.hw_tools.py
for all the operation relate to tools.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies in advance for the large number of comments. Overall, I think these are really good changes for hardware-observer and appreciate the time taken to plan and code this out 👍 Lot of my suggestions are just typos or re-wording to comments and doc-strings :)
src/charm.py
Outdated
], | ||
) | ||
self.exporter = Exporter(self.charm_dir) | ||
self.exporter_observer = exporter.Observer(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I want to think about what we're "observing" here. exporter.Observer
feels like I'm initializing a class that observes the exporter but it looks more like the class is observing the cos-agent
relation?
src/exporter.py
Outdated
@@ -0,0 +1,137 @@ | |||
"""Exporter relation observer.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your point. Introducing another layer for the charm to interface with the exporter functionality might be quite confusing.
So I think it's good to think about what's the core idea behind the src/exporter.py::Observer
and src/service.py::Exporter
and see if there's a chance we can combine or if we really need those 2 layers.
src/exporter.py
Outdated
"""Initialize the class.""" | ||
super().__init__(charm, EXPORTER_RELATION_NAME) | ||
self.charm = charm | ||
self.agent = Exporter(charm.charm_dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, we should choose a different name than agent
for our hw exporter.
src/exporter.py
Outdated
|
||
@property | ||
def agent_enabled(self) -> bool: | ||
"""Return True if cos-agent relation is not zero.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Might be nicer as Return True if cos-agent relation is present
src/charm.py
Outdated
resource_white_list, | ||
resource_black_list=self._stored.resource_install_status, # type: ignore[arg-type] | ||
) | ||
self._stored.resource_install_status = resource_install_status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe result
might be a better choice -- resource_install_result
return True | ||
|
||
|
||
def validate_checksum(support_version_infos: t.List[ToolVersionInfo], path: Path) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could argue that checksum.py
contains all the checksums for the various resource tools but doesn't need to contain the actual validation logic which could happen elsewhere.
REDFISH_HOST=redfish_creds.get("host", ""), | ||
REDFISH_USERNAME=redfish_creds.get("username", ""), | ||
REDFISH_PASSWORD=redfish_creds.get("password", ""), | ||
REDFISH_OPTIONS=redfish_options, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collecting all the redfish options into one structure is a really good idea 👍
src/service.py
Outdated
|
||
logger = getLogger(__name__) | ||
|
||
|
||
NUM_RETRIES = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could possible provide some more context to these constants. Maybe:
EXPORTER_HEALTH_RETRY_COUNT = 3
EXPORTER_HEALTH_RETRY_TIMEOUT = 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's within the context of service.py
"""Check the health of the exporter daemon and try to recover it if needed. | ||
|
||
This function perform health check on exporter daemon if the exporter | ||
is already installed. If it is somehow stopped, we should try to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what situation would the exporter have been stopped (and not crashed) and require a restart logic to fix it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe stopped by the operator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I wouldn't imagine the operator directly intervening with the lifecycle of the exporter service in the ideal case. Because then the operator would also know to start it back up again. But I can understand than things aren't ideal always and maybe we do need to have restarts to bring up the exporter. But this feels quite "hacky" for some reason.
redfish_host: "{{ REDFISH_HOST }}" | ||
redfish_username: "{{ REDFISH_USERNAME }}" | ||
redfish_password: "{{ REDFISH_PASSWORD }}" | ||
{% if REDFISH_OPTIONS.get('enable', False) %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have a hyphen (-
) before the closing %
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not, the indentation will be wrong:
port: 1000
level: debug
enable_collectors:
- 1
- 2
- 3
redfish_host: ""
redfish_username: ""
redfish_password: ""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I meant like this : {% if REDFISH_OPTIONS.get('enable', False) -%}
where it ends with -%}
similar to the other if
statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's the same as above. Somehow it thinks the redfish_host: ""
part of enable_collectors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add review for hw_tools.py
src/hw_tools.py
Outdated
package_source_mapping = t.Dict[str, str] | ||
|
||
@abstractmethod | ||
def add_repos(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We maybe don't need this if repo is not required. I suggest to use Mixin instead of abstractmethod method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reverted
src/hw_tools.py
Outdated
"""Check if the tool is valid or not.""" | ||
|
||
|
||
class APTStrategy(APTStrategyABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am -1 for this.
This make inheritance has too many levels. Suggest to re-design this part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I would suggest remove APTStrategyABC
and TRPStrategyABC
since they are not useful abstraction. APTStrategy
and TRPStrategy
are enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reverted
src/hw_tools.py
Outdated
success &= check_deb_pkg_installed(package) | ||
return success | ||
|
||
def validate_tool(self) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which {package}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some logs, but we will not be using it, since in the end the error will be a strategy failure
src/hw_tools.py
Outdated
"""Third party resource strategy class.""" | ||
"""Third party resource strategy abstract class.""" | ||
|
||
origin_path: Path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am -1 for this. We can't suppose that every third party tool follow this pattern to control it's resource. This maybe can't fit in some special case.
Put implement details in child class has some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reverted
src/hw_tools.py
Outdated
origin_path = Path("/opt/MegaRAID/storcli/storcli64") | ||
symlink_bin = TOOLS_DIR / HWTool.STORCLI.value | ||
|
||
class TPRStrategy(TPRStrategyABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1 here.
Same reason for too many levels of inheritance here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, then I would suggest remove APTStrategyABC
and TRPStrategyABC
since they are not useful abstraction. APTStrategy
and TRPStrategy
are enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reverted
src/hw_tools.py
Outdated
collector = EXPORTER_COLLECTOR_MAPPING.get(tool) | ||
if collector is not None: | ||
collectors += collector | ||
self._hw_collector_white_list = collectors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not choose to store some status in this class. The mapping logic actually is just mapping from HWTool
to another string. It's not expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, hw_tool_white_list
only this is expensive, updated
src/hw_tools.py
Outdated
@property | ||
def hw_tool_white_list(self) -> t.List[HWTool]: | ||
"""Define hardware tool white list.""" | ||
# cache the white list because it might be expensive to get |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a good practice for caching to store some information in the class. Every time you re-init this class the cache is gone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of cache instead class. A ttl cache or a decorator to decide when to use refresh maybe more make sense?
More to add is making class become no-status
can ignore a lot of hidden bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used cached_property
… from cos_agent.py
…y, and put it into ...". In particular, the refactoring in strategy classes are removed, and only added `check_status` and `validate_tool` methods. This partially reverts commit fbbd346.
installation status of the tools.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some more comments. Overall, I think we could use some nice refactoring with the hardware related modules. hw_tool.py
looks good adding the validate and install checks directly in the parent strategy classes 👍
redfish_host: "{{ REDFISH_HOST }}" | ||
redfish_username: "{{ REDFISH_USERNAME }}" | ||
redfish_password: "{{ REDFISH_PASSWORD }}" | ||
{% if REDFISH_OPTIONS.get('enable', False) %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I meant like this : {% if REDFISH_OPTIONS.get('enable', False) -%}
where it ends with -%}
similar to the other if
statements.
@@ -0,0 +1,101 @@ | |||
"""Wrapper for `model.Resources`.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are my thoughts:
current
hw_resources.py
- contains exception classes for resource check fails
- utils related to resource file handling eg:
check_file_exists
,validate_size
etc.
hardware.py
- constant containing supported raid storages
- utils for general hw checks. eg:
lshw
,get_bmc_address
- utils for hw verification,
{raid,bmc}_hw_verifier}
hw_tools.py
- utils related to package management, eg:
install_deb
,remove_deb
,check_deb_pkg_installed
- strategy classes for various tools
- HwToolHelper class
what i'm proposing
utils.py/ helpers.py
- This will be a general class with util methods contained in these helper classes similar to
HwToolHelper
ResourceHelper
: This contains mostly the helper methods fromhw_resources.py
--check_file_exists
,validate_size
etc.PackageHelper
-- will contain all theapt
anddeb
related functions --install_deb
,remove_deb
,check_deb_pkg_installed
,install_apt_package
HwHelper
: contains helper methods for hardware related tasks. --lshw
,get_bmc_address
,validate_redfish_credentials
HwVerifier
: contains the "verifier" methods fromhardware.py
and generate the whitelist --{raid,bmc}_hw_verifier}
- Also exception classes for resource checks.
hw_tools.py
-
This will now only have the strategy classes +
HwToolHelper
class -
The constant containing supported raid storages from
hardware.py
should go intoconfig.py
/constants.py
return True | ||
|
||
|
||
def validate_checksum(support_version_infos: t.List[ToolVersionInfo], path: Path) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main purpose of validating checksum is for the resource, so I put them together.
I agree with this approach. We could possibly rename checksum.py
to maybe tool_info.py
since it contains more than the checksums for each of the tools. The checksum validation functionality could go into the ResourceHelper
class I detailed in my other comment.
"""Check the health of the exporter daemon and try to recover it if needed. | ||
|
||
This function perform health check on exporter daemon if the exporter | ||
is already installed. If it is somehow stopped, we should try to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I wouldn't imagine the operator directly intervening with the lifecycle of the exporter service in the ideal case. Because then the operator would also know to start it back up again. But I can understand than things aren't ideal always and maybe we do need to have restarts to bring up the exporter. But this feels quite "hacky" for some reason.
change_set = set() | ||
model_config: Dict[str, Optional[str]] = dict(self.model.config.items()) | ||
for key, value in model_config.items(): | ||
if key not in self._stored.config or self._stored.config[key] != value: # type: ignore | ||
logger.info("Setting %s to: %s", key, value) | ||
self._stored.config[key] = value # type: ignore | ||
change_set.add(key) | ||
return change_set | ||
|
||
def get_redfish_options(self) -> Dict[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, maybe we could have redfish_configs
, get_redfish_configs
along with get_exporter_configs
? What do you think?
@@ -127,6 +99,10 @@ def name(self) -> HWTool: | |||
"""Name.""" | |||
return self._name | |||
|
|||
@abstractmethod | |||
def check_status(self) -> t.Dict[str, bool]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check_status
sounds quite ambiguous. Could we rename this like check_installed
or install_status
to make it more clear that this method lets you know if the tool was installed properly or not.
@dashmage , thanks for providing you thoughts, I am not against your idea of re-organizing the code base, but having everything inside a helper module isn't going to make the code base easier to understand (for example, you can broadly break down the code base into That's being said the purpose of this is PR is not refactoring, I am only doing the minimal refactoring that allows us to improve the charm's life cycle, makes it easier to debug by simplifying the logic in Looking back now, I think I can further reduce the refactoring, and leave the code base mostly unchanged at the expense of losing those benefits. |
This makes sense, maybe we can take up the refactoring discussion + work in another PR. It's better to have a smaller, clear PR that aims to achieve one thing 👍 |
@Pjack please let me know if I should extract only "enhance charm's life-cycle" part, and leave the refactoring to someone else. |
Closing this. Please check out #119 instead |
No description provided.