-
Notifications
You must be signed in to change notification settings - Fork 23.9k
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 Foreman inventory #19510
Add Foreman inventory #19510
Conversation
e5ccc10
to
734b77f
Compare
@dLobatog please sprinkle the docs https://github.com/theforeman/foreman_ansible_inventory into the .ini file. |
contrib/inventory/foreman.py
Outdated
self.facts = dict() # Facts of each host | ||
self.hostgroups = dict() # host groups | ||
|
||
def run(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.
Typically we organize code so that callers are closer to the bottom and callees are near the top (__init__
being the exception... it's always at the top). This organization puts methods like run()
near the bottom of the class and helpers above it.
734b77f
to
ac2fc49
Compare
contrib/inventory/foreman.py
Outdated
import os | ||
import re | ||
import requests | ||
from requests.auth import HTTPBasicAuth |
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.
Note, we try to organize imports into three blocks (all optional). stdlib imports at the top, then third party library imports (requests in this case), and finally any imports from ansible itself.
contrib/inventory/foreman.py
Outdated
cache.write(json_data) | ||
cache.close() | ||
|
||
@staticmethod |
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.
Helper functions like this (Ones that don't use self. Ones that could be marked @staticmethod if they were part of the class). should typically be placed at the toplevel. So far I see to_safe, json_format_dict, and push.
contrib/inventory/foreman.py
Outdated
try: | ||
import json | ||
except ImportError: | ||
import simplejson as json |
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 json library entered the python stdlib with python-2.6. Code that runs on the controller (which includes dynamic inventory scripts) need python-2.6 or greater. So there's no need to fallback to simplejson here. the json stdlib library should always be available.
contrib/inventory/foreman.py
Outdated
try: | ||
import ConfigParser | ||
except ImportError: | ||
import configparser as ConfigParser |
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.
Comment to explain python2 vs python3 would be good here.
contrib/inventory/foreman.py
Outdated
|
||
|
||
class ForemanInventory(object): | ||
config_paths = [ |
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 doesn't really matter since only a single instance of ForemanInventory is created in this script but config_paths should really be an instance attribute (created and given an initial value in __init__
) rather than a class attribute. Class attributes exist a single time per class. All instances of the class would share the same instance of that attribute. Instance attributes exist, one per instance and thus can change independently. Modifying config_paths later in the code is a tip-off that this should be an instance-attribute rather than a class-attribute.
Also, you can probably move the setting from an environment variable to be with this code when you do that. It would seem to make sense to keep that all together.
contrib/inventory/foreman.py
Outdated
import copy | ||
import os | ||
import re | ||
import requests |
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 would be better to avoid the requests lib, but if you cannot you should at least specify a min version.
Made some easy to fix style comments. The main logic looks like it will do what it's supposed to. It looks like there could be some larger changes that could be made (for instance, I think that --host only affects output here... It does not affect what has to be fetched from foreman). As long as you're continuing to maintain it, I don't think that's a blocker for getting this merged... more of a note about what could be fixed in the future. |
needs_revision |
ac2fc49
to
f1612e7
Compare
f1612e7
to
eef6b66
Compare
@abadger Thanks for taking a look - not a Python expert myself, but I think I've addressed most of the comments. The one I wasn't so sure about is @bcoca 's ask to pin requests to a version. I think 1.1 (what CentOS 6 has) is probably safe, but I'm not sure if you wanted something different. Hope this works - I'll see if I can remove requests from here at some point even, it was just.. convenient at the time. Thanks @chrismeyersfsu @abadger @bcoca for taking a look |
eef6b66
to
12216b2
Compare
BTW CI is picking on a problem on ./lib/ansible/modules/network/ldap_entry.py , not foreman.py |
contrib/inventory/foreman.py
Outdated
regex = "[^A-Za-z0-9\_]" | ||
return re.sub(regex, "_", word.replace(" ", "")) | ||
|
||
def json_format_dict(self, data, pretty=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.
This should be a @staticmethod and self removed.
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.
Actually, better would be to move this to the toplevel as a regular function
contrib/inventory/foreman.py
Outdated
else: | ||
return json.dumps(data) | ||
|
||
def push(self, d, k, v): |
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 should be an @staticmethod nad self removed.
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, make this a regular function
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.
Actually... Even better: use a defaultdict instead of this. This is how defaultdicts work:
from collections import defaultdict
foo = defaultdict(list)
foo[k].append(v)
contrib/inventory/foreman.py
Outdated
else: | ||
d[k] = [v] | ||
|
||
def run(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.
This should probably be the last method in the class.
contrib/inventory/foreman.py
Outdated
self.config_paths.append(os.path.expanduser(os.path.expandvars(env_value))) | ||
|
||
@staticmethod | ||
def to_safe(word): |
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.
Make this a regular function
contrib/inventory/foreman.py
Outdated
self._print_data() | ||
return True | ||
|
||
def _read_settings(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.
This method doesn't seem to do much. I would merge it into the run() method.
- read_settings and parse_cli_args don't seem related. They don't operate on the same instance attributes, for instance.
- It's a bit funny to have a private method (_read_settings()) calling public methods (read_settings() and parse_cli_args()) that aren't usable by anything else.
- If this is merged into run(), it will only add one more line there and will remove 5 lines overall.
contrib/inventory/foreman.py
Outdated
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): | ||
group_patterns = "[]" | ||
|
||
self.group_patterns = eval(group_patterns) |
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 didn't notice this before but you have eval here... What is that for? It's rather dangerous as it means if someone can get code into the config file for the foreman dynamic inventory, they can then execute whatever code they want as the user running ansible. We should get rid of this if at all possible.
contrib/inventory/foreman.py
Outdated
self.parse_cli_args() | ||
return True | ||
|
||
def _get_inventory(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.
No need for this to be a private method. rename to get_inventory().
contrib/inventory/foreman.py
Outdated
def _get_hosts(self): | ||
return self._get_json("%s/api/v2/hosts" % self.foreman_url) | ||
|
||
def _get_hostgroup_by_id(self, hid): |
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 doesn't seem to be called from anywhere. Should it be deleted?
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.
Yes, it was used to resolve Foreman host parameters on theforeman/foreman_ansible_inventory@9972f3f#diff-2b3c7ba3ec283a3b380cff70f61e0290R144 , not needed anymore
contrib/inventory/foreman.py
Outdated
if self.args.refresh_cache: | ||
self.update_cache() | ||
elif not self.is_cache_valid(): | ||
self.update_cache() |
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 better to write this as
if self.args.refresh_cache or not self.is_cache_valid():
self.update_cache()
This is probably what the order of methods should be inside of the class:
This puts the entrypoint at the bottom and then everything that helps it to run is above it. |
Okay, I think I've gone over this sufficiently that this is everything that needs to be changed. |
12216b2
to
f2bf34c
Compare
@abadger Thanks so much for the detailed review! I think I've gone through each of the things you've commented on , with the exception of #19510 (comment) - I didn't know what you referred to, it looks like a regular function already? The latest push contains the updates to your comments, including order of methods, and the improvements @wenottingham made here theforeman/foreman_ansible_inventory@7a55c62 |
@dLobatog Right now it's a method. My comment is to make it a function instead. |
This commit adds the foreman inventory based on https://github.com/theforeman/foreman_ansible_inventory and its configuration file.
f2bf34c
to
ac79854
Compare
@abadger Updated - the |
Merged to devel for inclusion in 2.3.0. Thaks @dLobatog ! |
Thanks @abadger for the review! |
ISSUE TYPE
COMPONENT NAME
external inventories
ANSIBLE VERSION
SUMMARY
Adds an inventory to the list of external inventories in contrib. This inventory allows Ansible users to query information from Foreman and Satellite 6 (https://github.com/theforeman/foreman_ansible_inventory).
Should I add the README here too?