-
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
module_utils/dimensiondata #17604
module_utils/dimensiondata #17604
Conversation
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 from cursory review, a couple of issues, I only made one comment of each but both are repeated in several spots.
import sys | ||
import ConfigParser | ||
from os.path import expanduser | ||
from libcloud.common.dimensiondata import API_ENDPOINTS |
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.
cannot assume non 'core' modules are in the system, this should attempt to import and then allow for the AnsibleModule class to report about the missing module in a 'friendly' way, see ec2.py for examples.
if is_uuid(locator): | ||
net_id = locator | ||
else: | ||
networks = driver.ex_list_network_domains(location=location) |
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.
don't all API calls need to capture exceptions?
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.
Exception handling is done in module code. This way we can keep the handling of the error specific to the module using the shared 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.
it seems inconsistent as you do capture it in some cases
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 in some cases the error is always the same so we return False to ease module development. In other cases there are multiple error possibilities from API, some of which we ignore in one case and not another.
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 above is an issue we can make all return the exception and consistently handle them in the 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.
+1 on raising a custom exception instead of failing silently.
if block_id is not 'False': | ||
try: | ||
block = driver.ex_get_public_ip_block(block_id) | ||
except DimensionDataAPIException: |
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: I'm assuming libcloud requires python >2.6, as such you can use Exception as e
, the exc_info is only required for modules that are 2.4 compatible, which most cloud modules cannot be due to library dependencies.
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 always use "except DimensionDataAPIException as e". This will be updated to reflect out standard process.
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 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.
a) was just note, not required to pass review
b) if libcloud already requries 2.6 we can make an exception in the check as we do for other cloud modules.
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 vote for consistency of "except DimensionDataAPIException as e"
Requests are listed in: ansible#17604 (review)
Rebased to ansible/devel |
Requests are listed in: ansible#17604 (review)
@relaxdiego @mikeoutland This PR was added to ansible/community#124 we have discussed this and as we are now in feature freeze for 2.2 this (and related PRs) will not be reviewed/merged till after Ansible 2.2 has reached rc1 (release candidate 1). At that point we will have branched and allow new PRs to be merged. |
# | ||
# Common methods to be used by versious module components | ||
import os | ||
import 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.
This formulation of importing configparser is not compatible with python3. You can use
from ansible.module_utis.six.moves import 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.
Sorry, just checking - do you mean:
from ansible.module_utis.six.moves import configparser
or
from ansible.module_utis.six.moves import ConfigParser
(I've never seen this code before so it's a lot to take in, but it looks like the latter rather than the former?)
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.
BTW, apologies for asking potentially stupid questions, but I'm jumping into this blind - it's been a while since I've written anything fancier than a simple script in Python and I'm a little rusty:
I can find ansible/module_utils/six.py
but I'm not sure where moves
comes from?
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.
BTW, if I'm importing it as configparser
I assume references to the ConfigParser
class are still valid (again, sorry if this is a stupid question)?
region_list = filter(lambda i: i.startswith('dd-'), all_regions) | ||
|
||
# Strip prefix | ||
regions = [region[3:] for region in region_list] |
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.
List comprehensions and generator expressions are more pythonic than using filter. This one can be rewritten as a single listcomprehension or a combination of as two steps:
# One combined list comprehension
regions = [r[3:] for r in all_regions if r.startswith('dd-')]
# two steps, using a generator expression
region_list = (r for r in all_regions if r.startswith('dd-'))
regions = [r[3:] for r in region_list]
Get a network domain object by its name | ||
""" | ||
networks = driver.ex_list_network_domains(location=location) | ||
found_networks = filter(lambda x: x.name == name, networks) |
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 usage of filter won't work on python3. filter on python3 will return a generator which will evaluate to true in the conditional below. Use a list comprehension so that found_networks ends up with an empty list on python3 which will evaluate to 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.
Same change needs to be made to many other uses of filter.
Get public IP block details | ||
""" | ||
# Block ID given, try to use it. | ||
if block_id is not '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.
I don't think this works? block_id defaults to the boolean False in the parameters to this function. So you probably need to check:
if block_id is not False:
rather than checking against a string.
else: | ||
blocks = list_public_ip_blocks(module, driver, network_domain) | ||
if blocks is not False: | ||
block = filter(lambda x: x.base_ip == base_ip, blocks)[0] |
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 will fail on python3 for a similar reason as the conditional check noted above. filter returns a generator. The generator can't be indexed.
Use a list comprehension instead:
block = [b for b in blocks if b.base_ip == base_ip]
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.
Thanks - I've replaced all uses of filter
with comprehensions.
block = filter(lambda x: x.base_ip == base_ip, blocks)[0] | ||
else: | ||
module.exit_json(changed=False, msg="IP block starting with " | ||
"'%s' does not exist." % base_ip) |
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 message here looks like a failure case rather than an exit case... Perhaps it should use module.fail_json or the message should be changed?
Stylistically, exit_json() cases should retun back to the main function in the module and let the exit_json() occur there. It's more flexible to changing needs inside of the module than calling exit_json() here.
get_credentials will now look in environment, dotfile, and module configuration for credentials (in that order).
This method will get user_id and key from module configuration, environment, or dotfile. | ||
Environment takes priority. | ||
Get user_id and key from module configuration, environment, or dotfile. | ||
Order of priority is environment, dotfile, 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.
Near as I can tell the order of priority here is module, environment, dotfile. Module should be first, so the coded order is fine but the documentation here should agree with 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.
Sorry, I think the current logic will only fall back to module if environment variable has not been specified: https://github.com/ansible/ansible/pull/17604/files/6c412fbeefa07e4296b33ec7544c5b1a69a6dcd8..85164a272274514877a6369877e851461a5acf0b#diff-0b1c579a3ccbeed5e582e7693e046cb6R112
I'll change the order, though, to put module first. Give me 5 mins.
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.
Done.
@tintoy. Looks good. Just a disagreement between the documentation and the code to straighten out. Once that's fixed, this is ready to merge. |
…environment or dotfile (ansible#17604).
Thanks again for your patience, BTW. |
Merged to devel! thanks for your patience as well :-) |
Great! I'll start looking at the first PR that depends on this :) |
ISSUE TYPE
New Module Pull Request
COMPONENT NAME
dimensiondata
ANSIBLE VERSION
SUMMARY
This change contains common code required by the dimensiondata modules.
SPECIAL NOTE
apache-libcloud
REQUIRED BY