-
Notifications
You must be signed in to change notification settings - Fork 23.7k
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
Add ManageIQ alert profiles module #32354
Conversation
@abellotti @cben @dkorn @gtanzillo @yaacov @zgalor As a maintainer of a module in the same namespace this new module has been submitted to, your vote counts for shipits. Please review this module and add |
description: | ||
- absent - alert profile should not exist, | ||
- present - alert profile should exist, | ||
- list - return a list of alert policies. |
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.
typo? "list of alert profiles"?
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 was a typo. Fixed now. (I'll squash the commits before this PR will be merged)
|
||
# assign / unassign the alert policies, if needed | ||
|
||
if to_remove: |
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 may accidentally remove policies user did not explicitly specify state: "absent" for policies that currently exist but no the list the user gave.
for example:
if I have policies A and B
and I ask in the playbook to make sure A is present.
the play book may inadvertently delete policy B.
p.s.
you may want to specify new actions to add and remove policies from a profile
e.g. "assign and set policies" ?
cc @cben @joelddiaz ?
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.
from my experience with configuration management tools similar to ansible, when you define something using the playbook you want it to act like this - to make sure the configuration on the server is 100% in sync with the automated configuration.
"absent" and "present" are the states of the profile itself, when the state is "present" that means "it needs to 1) exist 2) be configured exactly how it's described in the playbook".
that's my take on it, but I'll be happy to change it if the ops people decide they want it to behave differently.
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.
as someone using this module, i would be surprised if the behavior when creating a new alert profile A, a separate unrelated profile B disappeared.
a different way of saying this is if the product ships with profiles X and Y, then while creating a new profile A, i shouldn't have to provide (or know about) X and Y to the ansible module call when all i want to do is create/edit/delete my own alert profile A.
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 think you misunderstood the question. This has nothing to do with "unrelated alert profiles", let me paraphrase:
Assume you have manually added an alert profile X, and assigned the alerts A, B and C to it.
If run an ansible playbook that defines alert profile X with only A and B assigned, it will remove C from the existing profile. Is that a kind of behaviour you expect?
If you have a profile Y that is not defined in the playbook, the module will (obviously) not touch 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.
I absolutely did misunderstand then.
For the primary use case, I think we do want an Ansible call to create/update profile X with alerts A and B to make sure that only alerts A & B are associated with profile X.
There is an edge case where we may want to surgically append/remove alerts from profile X, but I don't think it is critical to support this use case at this time.
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.
Okay, so the primary use case is implemented by the module. The edge case of "surgically removing/appending" is not supported at the moment, but should be easy to add in the future if it'll ever be needed.
- absent - alert profile should not exist, | ||
- present - alert profile should exist, | ||
- list - return a list of alert profiles. | ||
required: 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.
Not a blocker but required: False
is the default, so no need to document it.
response = self.client.get(self.url + '?expand=alert_definitions,resources') | ||
except Exception as e: | ||
self.module.fail_json(msg="Failed to query alert profiles: {error}".format(error=e)) | ||
return response.get('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.
If you like to replace None
with []
, it sugguest to change it to response.get('resources') or []
. The difference is, currently with this code, []
will only returned if there is no resources
key in response
shipit |
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.
LGTM
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 technical comments. overall structure good.
|
||
EXAMPLES = ''' | ||
- name: List alert policies in ManageIQ | ||
manageiq_alert_policies: |
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.
typo, manageiq_alert_profiles
default: 'present' | ||
description: | ||
description: | ||
- The unique alert profile description in ManageIQ. |
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.
unique globally or within resource_type?
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 thought it was globally unique, just like with alerts, but apparently alert profiles don't enforce uniqueness of the description. The module assumes its globally unique, but this is not necessarily true... I've sent an email to the relevant ManageIQ people to see if this can be changed.
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 the new plan is to use name
which is unique.
Creating from ansible, we can control both name and description.
Creating from UI only asks for description; it currently sets name to a GUID, will be changed to equal description.
|
||
|
||
# TODO: I copied this from yaacov's ansible/ansible PR 31233. It should be removed | ||
# from here once that PR is merged. |
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.
now in utils, can drop from here.
self.module.fail_json(msg="Failed to query alert profiles: {error}".format(error=e)) | ||
return response.get('resources') or [] | ||
|
||
def get_alert_policies(self, alert_policies): |
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.
as discussed previously, no such thing "alert policies". every mention of "policy/ies" should be renamed...
|
||
# figure out which policies we need to assign / unassign | ||
# alert polices listed by the user: | ||
new_alert_policies = set(self.get_alert_policies(new_profile['alert_policies'])) |
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.
"new" may sound as "to be added", suggest "desired" instead.
result = self.client.post(result['results'][0]['href'] + '/alert_definitions', | ||
action="assign", resources=policies) | ||
except Exception as e: | ||
# TODO question - should we rollback the creation if assignment failed? |
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 no need to.
Each step is idempotent, we failed to reach desired state but we got closer.
But mostly, it's just easier ;-)
""" | ||
# find all alert policies to add to the profile | ||
# we do this first to fail early if one is missing. | ||
policies = [dict(href=href) for href in self.get_alert_policies(profile['alert_policies'])] |
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 dict(href=href)
building repeats everywhere... can you move it inside either assign_or_unassign?
or get_alert_policies()
?
# now that it has been created, we can assign the policies | ||
try: | ||
result = self.client.post(result['results'][0]['href'] + '/alert_definitions', | ||
action="assign", resources=policies) |
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.
any reason not to use assign_or_unassign()
here?
# existing profile has different notes | ||
profile_dict['set_data'] = dict(notes=new_profile['notes']) | ||
|
||
if new_profile['notes'] and ('set_data' not in old_profile or 'notes' not in old_profile['set_data']): |
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 condition, and previous one, are complicated.
partly caused by set_data
asymmetry.
can you first compute 2 vars with existing notes and new notes (using None or '' where missing),
and then just check if old_notes != new_notes:
?
Also, please open manageiq-api issues for each read vs write format asymmetry in the API. There are too many of these :-(
existing_profile = manageiq.find_collection_resource_by("alert_definition_profiles", | ||
description=description) | ||
|
||
# we need to add or update the alert policy |
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 again, and below L332, "alert policy" is not alert but typo for alert profile
@cben can you re-review please? |
if old_profile['set_data']['notes'] != new_profile['notes']: | ||
# existing profile has different notes | ||
profile_dict['set_data'] = dict(notes=new_profile['notes']) | ||
old_notes = old_profile['set_data']['notes'] |
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.
above 3 lines can be written as:
old_notes = old_profile.get('set_data', {}).get('notes')
old_notes = old_profile['set_data']['notes'] | ||
|
||
if desired_profile['notes'] != old_notes: | ||
# existing profile has no notes, but we have notes |
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 think this comment is not precise now, this also happens when existing has some old_notes, new has empty string, or both old/new non-empty but different... Can just drop it, or "# notes needs to be updated".
I addressed review feedback and squashed the commits. Now that ManageIQ/manageiq-api#149 is merged, this module is ready for a final review (and eventually for being merged). @cben @yaacov please take another look when you have the time. |
The test
|
alerts = [] | ||
for alert_description in alert_descriptions: | ||
alert = self.manageiq.find_collection_resource_or_fail("alert_definitions", | ||
description=alert_description) |
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 think this should now match by name
(and method arg should be named alert_names
)
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.
Ignore this, I confused alert profile — matched by name, because that's the unique field — with alerts that are matched by description, which is unique in that table and more user friendly.
response = self.client.get(self.url + '?expand=alert_definitions,resources') | ||
except Exception as e: | ||
self.module.fail_json(msg="Failed to query alert profiles: {error}".format(error=e)) | ||
return response.get('resources') or [] |
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.
python nitpick: if you just want to return [] for missing 'resource' key, write .get('resources', [])
.
As currently written is effectively .get('resource', None) or []
, which will also accept "resources": null
and other any "falsey" values like "resources": ""
. Harmless but one day someone will wonder "is this intentional, can server return non-arrays 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.
oops this is exactly opposite to what @resmo recommended previously #32354 (comment)
I don't really care, LGTM from me on either form.
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.
LGTM
shipit |
bot_status |
Componentslib/ansible/modules/remote_management/manageiq/manageiq_alert_profiles.py Metadatawaiting_on: maintainer |
shipit 🚢 |
bot_status |
Componentslib/ansible/modules/remote_management/manageiq/manageiq_alert_profiles.py Metadatawaiting_on: maintainer |
LGTM 👍 |
@resmo could you review please? |
Sorry to bother again The module uses |
needs_revision |
version_added: '2.5' | ||
author: Elad Alfassa (ealfassa@redhat.com) | ||
description: | ||
- The manageiq_alert_profiles module supports listing, adding, updating and deleting alert profiles in ManageIQ. |
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.
listing ?
shipit |
1 similar comment
shipit |
bot_status |
merging. |
Thanks all of you for the hard work! |
SUMMARY
ManageIQ is an open source management platform for Hybrid IT.
This change is adding:
manageiq_alert_profiles module, responsible for alert profiles management in ManageIQ.
(alert profiles are collections of alert policies).
All the modules, including docs, tests and usage examples can be found here
Currently, the only requirement for the module is manageiq-api-client-python,
the module also requires ManageIQ/manageiq-api#149 in the ManageIQ server.
ISSUE TYPE
COMPONENT NAME
manageiq_alert_profiles.py (module)
ANSIBLE VERSION