自制Ansible模块有#34;设置错误:没有名为FabricVcon"

时间:2017-10-31 19:44:07

标签: python python-2.7 automation ansible cisco

问题

在测试我自己的Ansible模块时,我遇到了这个特殊的错误:"setup error: No module named FabricVcon "
然而,当在Python中测试模块的基本API(也是自制的)时,代码可以无错误地工作。我很困惑,因为我用于其他基本API的Ansible Module模板是相同的,只是交换了相关信息。所有以前的类似模块都有效。为清楚起见,我开发的Ansible模块使用我开发的API Python文件。该Python API文件实现了官方支持的思科Python库。

编辑:部分问题出在错误代码中。 ucsmsdk基类是FabricVCon,而错误告诉我它找不到FabricVcon。区别在于名称中的第二个C.我在发布问题之前对代码进行了更改,以确保使用了FabricVCon,但错误仍然存​​在。

上下文

我目前正致力于使用Ansible通过Python中的ucsmsdk自动创建和修改服务配置文件模板。我一直在为自己的CiscoUcs团队的ucsm_apis和ucsm-ansible回购做出贡献,他们使用他们的文件作为我自己应该如何工作的指南。我安装了自己的repos中的文件,并且正常运行,除了这个。下面我已经在问题中添加了相关代码,但是如果您发现需要更多代码,请参阅GitHub account上的两个分析

代码

test_fabric.yml

---
- 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"

fabric_vcon.py

(最后评论是我对Python代码本身的测试)

"""
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")
"""

fabric_vcon_module.py

#!/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()

1 个答案:

答案 0 :(得分:0)

解决方案

事实证明,Ansible正在/~/.local/lib/python2.7/site-packages/ucsm_apis-0.9.0.0-py2.7.egg/ucsm_apis查找ucsm_api文件,而不是 /usr/lib/python2.7/site-packages/ucsm_apis-0.9.0.0-py2.7.egg/ucsm_apis首先。

删除本地版本并从机器版本复制解决了这个问题。

我是如何发现它的

我在fabric_vcon_module文件的import pdb;pdb.set_trace()函数中嵌入了行setup_fabric_vcon(server, module),就在import语句之后。

运行python /[proper_path]/fabric_vcon_module.py /[proper_path]/args_fabric.json并在pdb中逐行移动显示错误来自fabric_vcon_create(handle=server, **args_mo)行。

我重新启动pdb以进入该函数,并且对函数的调用向我显示了正在访问的库的文件位置。

JSON文件

{
    "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*"
    }
}