When testing my own Ansible module, I run into this particular error:"setup error: No module named FabricVcon "
Yet when testing the module's base API (also homemade) in Python, the code works error-free. I'm quite confused because the Ansible Module template I have been using for the other base APIs is the same, just with the relevant information swapped in. All of the previous similar modules work. For clarity's sake, the Ansible module I developed uses the API Python file I developed. That Python API file implements an officially supported Cisco Python library.
Edit: Part of the issue is in the error code itself. The ucsmsdk base class is FabricVCon, while the error tells me it cannot find FabricVcon. The difference is the second C in the name. I have made changes in my code before posting the question to ensure that FabricVCon was used, yet the error persists.
I am currently working on automating Service Profile Template creation and modification via the ucsmsdk in Python with Ansible. I have been contributing to my own forks of the CiscoUcs team's repos of ucsm_apis and ucsm-ansible, using their files as a guide for how my own should work. I have the files from my own repos installed and functioning properly, with the exception of this one. Below I've added the relevant code to the issue, but if you find you need more to work with, please see the two forks on my GitHub account
---
- name: test the test the fabric_vcon_module
connection: local
hosts: localhost
tasks:
- name: Make the fabric vcon now
fabric_vcon_module:
id: "1"
ls_server_dn: "org-root/org-VM/ls-SCALEIO"
fabric: "NONE"
inst_type: "manual"
placement: "physical"
select: "all"
share: "shared"
transport: "ethernet, fc"
state: present
ucs_ip: "IP"
ucs_username: "USER"
ucs_password: "PWD"
(Commented out at the end is my test of the Python code itself)
"""
This module intends on creating higher level api calls for establishing an
Fabric Vcon
"""
from ucsmsdk.ucsexception import UcsOperationError
def fabric_vcon_create(handle, id, ls_server_dn, fabric='NONE',
inst_type="manual", placement="physical", select="all",
share="shared", transport="ethernet", **kwargs):
"""
create fabric vcon
Args:
handle (UcsHandle)
id (string): '1' or '2' or '3' or '4'
ls_server_dn (string):
fabric (string): 'A' or 'B' or 'any' or 'NONE'
inst_type (string): 'auto' or 'manual' or 'policy'
placement (string): 'auto' or 'physical'
select (string): 'all' or 'assigned-only' or 'dynamic-only' or
'exclude-dynamic' or 'exclude-unassigned' or
'exclude-usnic' or 'unassigned-only' or 'usnic-only'
share (string): 'different-transport' or 'exclusive-only' or
'exclusive-preferred' or 'same-transport' or 'shared'
transport (string):
Returns:
FabricVCon: managed object
Raises:
UcsOperationError: if LsServer is not present
Example:
"""
from ucsmsdk.mometa.fabric.FabricVCon import FabricVCon
obj = handle.query_dn(ls_server_dn)
if not obj:
raise UcsOperationError("fabric_vcon_create", "LsServer '%s' does not \
exist" % ls_server_dn)
mo = FabricVCon(parent_mo_or_dn=obj, id=id, fabric=fabric,
inst_type=inst_type, placement=placement, select=select,
share=share, transport=transport)
mo.set_prop_multiple(**kwargs)
handle.add_mo(mo, modify_present=True)
handle.commit()
return mo
def fabric_vcon_get(handle, id, ls_server_dn, caller="fabric_vcon_get"):
"""
get fabric vcon
Args:
handle (UcsHandle)
id (string):
ls_server_dn (string):
caller (string):
Returns:
FabricVCon: managed object
Raises:
UcsOperationError: if the FabricVCon is not present
Example:
"""
dn = ls_server_dn + "/vcon-" + id
mo = handle.query_dn(dn)
if mo is None:
raise UcsOperationError(caller, "FabricVCon '%s' does not exist" % dn)
return mo
def fabric_vcon_exists(handle, id, ls_server_dn, **kwargs):
"""
checks if fabric vcon exists
Args:
handle(UcsHandle)
id (string): ls server name
ls_server_dn (string): location to place ls server
**kwargs: key-value pair of managed object(MO) property and value, Use
'print(ucscoreutils.get_meta_info(<classid>).config_props)'
to get all configurable properties of class
Returns:
(True/False, FabricVCon mo/None)
Raises:
None
Example:
"""
try:
mo = fabric_vcon_get(handle=handle, id=id, ls_server_dn=ls_server_dn,
caller="fabric_vcon_exists")
except UcsOperationError:
return (False, None)
mo_exists = mo.check_prop_match(**kwargs)
return (mo_exists, mo if mo_exists else None)
def fabric_vcon_modify(handle, id, ls_server_dn, **kwargs):
"""
modifies fabric vcon
Args:
handle (UcsHandle)
id (string):
ls_server_dn (string):
**kwargs:
Returns:
FabricVCon: managed object
Raises:
UcsOperationError: if FabricVCon is not present
Example:
"""
mo = fabric_vcon_get(handle=handle, id=id, ls_server_dn=ls_server_dn,
caller="fabric_vcon_modify")
mo.set_prop_multiple(**kwargs)
handle.set_mo(mo)
handle.commit()
return mo
def fabric_vcon_delete(handle, id, ls_server_dn):
"""
deletes fabric vcon
Args:
handle (UcsHandle)
id (String): ls server name
ls_server_dn (string): ls server's full name
Returns:
None
Raises:
UcsOperationError: if FabricVCon is not present
Example:
"""
mo = fabric_vcon_get(handle=handle, id=id, ls_server_dn=ls_server_dn,
caller="fabric_vcon_delete")
handle.remove_mo()
handle.commit()
"""
if __name__ == "__main__":
from ucsmsdk.ucshandle import UcsHandle
handle = UcsHandle("10.94.254.136","ansible","elbisna1*")
handle.login()
fabric_vcon_create(handle, "4" ,"org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO", fabric='NONE',
inst_type="manual", placement="physical", select="all",
share="shared", transport="ethernet")
print fabric_vcon_exists(handle, "4" ,"org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO")
print fabric_vcon_get(handle, "4" ,"org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO")
fabric_vcon_modify(handle, "4" ,"org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO", inst_type="auto")
fabric_vcon_delete(handle, "4" ,"org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO")
"""
#!/usr/bin/env python
from ansible.module_utils.basic import *
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cisco_ucs_ls_server
short_description: configures ls server on a cisco ucs server profile
version_added: 0.9.0.0
description:
- configures ls server on a cisco ucs server profile
options:
state:
description:
- if C(present), will perform create/add/enable operation
- if C(absent), will perform delete/remove/disable operation
required: false
choices: ['present', 'absent']
default: "present"
id:
version_added: "1.0(1e)"
description: boot policy name
required: true
choices: ['1', '2', '3', '4']
ls_server_dn:
version_added: "1.0(1e)"
description:
required: false
fabric:
version_added: "1.0(1e)"
description:
required: false
choices: ['A', 'B', 'any', 'NONE']
inst_type:
version_added: "1.0(1e)"
description:
required: false
choices: ['auto', 'manual', 'policy']
placement:
version_added: "1.0(1e)"
description:
required: false
choices: ['auto', 'physical']
select:
version_added: "1.0(1e)"
description:
required: false
choices: ['all', 'assigned-only', 'dynamic-only', 'exclude-dynamic', 'exclude-unassigned',
'exclude-usnic', 'unassigned-only', 'usnic-only']
share:
version_added: "1.0(1e)"
description:
required: false
choices: ['different-transport', 'exclusive-only', 'exclusive-preferred', 'same-transport', 'shared']
transport:
version_added: "1.0(1e)"
description:
required: false
requirements: ['ucsmsdk', 'ucsm_apis']
author: "Cisco Systems Inc(ucs-python@cisco.com)"
'''
EXAMPLES = '''
- name:
fabric_vcon_module:
id: "1"
ls_server_dn: "org-root/ls-spt-test"
fabric: "B"
inst_type: "manual"
select: "assigned-only"
share: "different-transport"
state: "present"
ucs_ip: "192.168.1.1"
ucs_username: "admin"
ucs_password: "password"
'''
#Arguments object for the Managed Object in question
def _argument_mo():
return dict(
id=dict(required=True, type='str', choices=['1', '2', '3', '4']),
ls_server_dn=dict(required=True, type='str'),
fabric=dict(type='str', choices=['A', 'B', 'any', 'NONE'], default="NONE"),
inst_type=dict(type='str', choices=['auto', 'manual', 'policy'], default="manual"),
placement=dict(type='str', choices=['auto', 'physical'], default="physical"),
select=dict(type='str', choices=['all', 'assigned-only', 'dynamic-only', 'exclude-dynamic',
'exclude-unassigned', 'exclude-usnic', 'unassigned-only', 'usnic-only'],
default="all"),
share=dict(type='str', choices=['different-transport', 'exclusive-only',
'exclusive-preferred', 'same-transport', 'shared'], default="shared"),
transport=dict(type='str', default="ethernet")
)
#Arguments object unique to the Ansible Module
def _argument_custom():
return dict(
state=dict(default="present",
choices=['present', 'absent'],
type='str'),
)
#Arguments object related to the UcsHandle
def _argument_connection():
return dict(
# UcsHandle
ucs_server=dict(type='dict'),
# Ucs server credentials
ucs_ip=dict(type='str'),
ucs_username=dict(default="admin", type='str'),
ucs_password=dict(type='str', no_log=True),
ucs_port=dict(default=None),
ucs_secure=dict(default=None),
ucs_proxy=dict(default=None)
)
#Creates the AnsibleModule object with the all arguments
def _ansible_module_create():
argument_spec = dict()
argument_spec.update(_argument_connection())
argument_spec.update(_argument_mo())
argument_spec.update(_argument_custom())
return AnsibleModule(argument_spec,
supports_check_mode=True)
#Retrieves non-None mo properties
def _get_mo_params(params):
from ansible.module_utils.cisco_ucs import UcsConnection
args = {}
for key in _argument_mo():
if params.get(key) is None:
continue
args[key] = params.get(key)
return args
def setup_fabric_vcon(server, module):
from ucsm_apis.service_profile.fabric_vcon import fabric_vcon_create
from ucsm_apis.service_profile.fabric_vcon import fabric_vcon_exists
from ucsm_apis.service_profile.fabric_vcon import fabric_vcon_delete
ansible = module.params
args_mo = _get_mo_params(ansible)
exists, mo = fabric_vcon_exists(handle=server, **args_mo)
if ansible["state"] == "present":
if module.check_mode or exists:
return not exists
fabric_vcon_create(handle=server, **args_mo)
else:
if module.check_mode or not exists:
return exists
fabric_vcon_delete(server, mo.name, args_mo['org_dn'])
return True
#Attempts to run the above method and provides error handling if it fails
def setup(server, module):
result = {}
err = False
try:
result["changed"] = setup_fabric_vcon(server, module)
except Exception as e:
err = True
result["msg"] = "setup error: %s " % str(e)
result["changed"] = False
return result, err
#Creates the module and makes the connections, only real work is done in setup
def main():
from ansible.module_utils.cisco_ucs import UcsConnection
module = _ansible_module_create()
conn = UcsConnection(module)
server = conn.login()
result, err = setup(server, module)
conn.logout()
if err:
module.fail_json(**result)
module.exit_json(**result)
if __name__ == '__main__':
main()
It turns out that Ansible was looking in /~/.local/lib/python2.7/site-packages/ucsm_apis-0.9.0.0-py2.7.egg/ucsm_apis
for the ucsm_api files rather than
/usr/lib/python2.7/site-packages/ucsm_apis-0.9.0.0-py2.7.egg/ucsm_apis
first.
Deleting the local version and copying over from the machine version resolved the issue.
I embedded the line import pdb;pdb.set_trace()
in the setup_fabric_vcon(server, module)
function of the fabric_vcon_module file, just after the import statements.
Running python /[proper_path]/fabric_vcon_module.py /[proper_path]/args_fabric.json
and moving line by line in pdb showed that the error came from the fabric_vcon_create(handle=server, **args_mo)
line.
I reran pdb to step into the function and the calls to the function showed me the file location of the library being accessed.
{
"ANSIBLE_MODULE_ARGS": {
"id": "1",
"ls_server_dn": "org-root/org-DHS-VM/ls-DS-ESX-C2-SCALEIO",
"fabric": "NONE",
"inst_type": "manual",
"placement": "physical",
"select": "all",
"share": "shared",
"transport": "ethernet, fc",
"state": "present",
"ucs_ip": "10.94.254.136",
"ucs_username": "ansible",
"ucs_password": "elbisna1*"
}
}