Migrating DellEMC collections from community.general collections (#1699)

* migration modification for dellemc collections

* removing dellemc collections

* Update changelogs/fragments/948-dellemc-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update 948-dellemc-migration-removal.yml

* Update 948-dellemc-migration-removal.yml

* Update runtime.yml

* Update meta/runtime.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update runtime.yml

* deleted symlink entries

* Update 948-dellemc-migration-removal.yml

* Update changelogs/fragments/948-dellemc-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/948-dellemc-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
Rajeev Arakkal 2021-02-01 09:06:58 -08:00 committed by GitHub
commit 8afdd23be4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 23 additions and 1433 deletions

View file

@ -1 +0,0 @@
./remote_management/dellemc/idrac_firmware.py

View file

@ -1 +0,0 @@
./remote_management/dellemc/idrac_server_config_profile.py

View file

@ -1 +0,0 @@
./remote_management/dellemc/ome_device_info.py

View file

@ -1,207 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 2.0
# Copyright (C) 2018-2019 Dell Inc. or its subsidiaries. All Rights Reserved.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: idrac_firmware
short_description: Firmware update from a repository on a network share (CIFS, NFS).
description:
- Update the Firmware by connecting to a network share (either CIFS or NFS) that contains a catalog of
available updates.
- Network share should contain a valid repository of Update Packages (DUPs) and a catalog file describing the DUPs.
- All applicable updates contained in the repository are applied to the system.
- This feature is available only with iDRAC Enterprise License.
options:
idrac_ip:
description: iDRAC IP Address.
type: str
required: True
idrac_user:
description: iDRAC username.
type: str
required: True
idrac_password:
description: iDRAC user password.
type: str
required: True
aliases: ['idrac_pwd']
idrac_port:
description: iDRAC port.
type: int
default: 443
share_name:
description: CIFS or NFS Network share.
type: str
required: True
share_user:
description: Network share user in the format 'user@domain' or 'domain\\user' if user is
part of a domain else 'user'. This option is mandatory for CIFS Network Share.
type: str
share_password:
description: Network share user password. This option is mandatory for CIFS Network Share.
type: str
aliases: ['share_pwd']
share_mnt:
description: Local mount path of the network share with read-write permission for ansible user.
This option is mandatory for Network Share.
type: str
required: True
reboot:
description: Whether to reboots after applying the updates or not.
type: bool
default: false
job_wait:
description: Whether to wait for job completion or not.
type: bool
default: true
catalog_file_name:
required: False
description: Catalog file name relative to the I(share_name).
type: str
default: 'Catalog.xml'
requirements:
- "omsdk"
- "python >= 2.7.5"
author: "Rajeev Arakkal (@rajeevarakkal)"
'''
EXAMPLES = """
---
- name: Update firmware from repository on a Network Share
community.general.idrac_firmware:
idrac_ip: "192.168.0.1"
idrac_user: "user_name"
idrac_password: "user_password"
share_name: "192.168.0.0:/share"
share_user: "share_user_name"
share_password: "share_user_pwd"
share_mnt: "/mnt/share"
reboot: True
job_wait: True
catalog_file_name: "Catalog.xml"
"""
RETURN = """
---
msg:
type: str
description: Over all firmware update status.
returned: always
sample: "Successfully updated the firmware."
update_status:
type: dict
description: Firmware Update job and progress details from the iDRAC.
returned: success
sample: {
'InstanceID': 'JID_XXXXXXXXXXXX',
'JobState': 'Completed',
'Message': 'Job completed successfully.',
'MessageId': 'REDXXX',
'Name': 'Repository Update',
'JobStartTime': 'NA',
'Status': 'Success',
}
"""
from ansible_collections.community.general.plugins.module_utils.remote_management.dellemc.dellemc_idrac import iDRACConnection
from ansible.module_utils.basic import AnsibleModule
try:
from omsdk.sdkcreds import UserCredentials
from omsdk.sdkfile import FileOnShare
HAS_OMSDK = True
except ImportError:
HAS_OMSDK = False
def _validate_catalog_file(catalog_file_name):
normilized_file_name = catalog_file_name.lower()
if not normilized_file_name:
raise ValueError('catalog_file_name should be a non-empty string.')
elif not normilized_file_name.endswith("xml"):
raise ValueError('catalog_file_name should be an XML file.')
def update_firmware(idrac, module):
"""Update firmware from a network share and return the job details."""
msg = {}
msg['changed'] = False
msg['update_status'] = {}
try:
upd_share = FileOnShare(remote=module.params['share_name'] + "/" + module.params['catalog_file_name'],
mount_point=module.params['share_mnt'],
isFolder=False,
creds=UserCredentials(
module.params['share_user'],
module.params['share_password'])
)
idrac.use_redfish = True
if '12' in idrac.ServerGeneration or '13' in idrac.ServerGeneration:
idrac.use_redfish = False
apply_update = True
msg['update_status'] = idrac.update_mgr.update_from_repo(upd_share,
apply_update,
module.params['reboot'],
module.params['job_wait'])
except RuntimeError as e:
module.fail_json(msg=str(e))
if "Status" in msg['update_status']:
if msg['update_status']['Status'] == "Success":
if module.params['job_wait']:
msg['changed'] = True
else:
module.fail_json(msg='Failed to update firmware.', update_status=msg['update_status'])
return msg
def main():
module = AnsibleModule(
argument_spec={
"idrac_ip": {"required": True, "type": 'str'},
"idrac_user": {"required": True, "type": 'str'},
"idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True},
"idrac_port": {"required": False, "default": 443, "type": 'int'},
"share_name": {"required": True, "type": 'str'},
"share_user": {"required": False, "type": 'str'},
"share_password": {"required": False, "type": 'str', "aliases": ['share_pwd'], "no_log": True},
"share_mnt": {"required": True, "type": 'str'},
"catalog_file_name": {"required": False, "type": 'str', "default": "Catalog.xml"},
"reboot": {"required": False, "type": 'bool', "default": False},
"job_wait": {"required": False, "type": 'bool', "default": True},
},
supports_check_mode=False)
try:
# Validate the catalog file
_validate_catalog_file(module.params['catalog_file_name'])
# Connect to iDRAC and update firmware
with iDRACConnection(module.params) as idrac:
update_status = update_firmware(idrac, module)
except (ImportError, ValueError, RuntimeError) as e:
module.fail_json(msg=str(e))
module.exit_json(msg='Successfully updated the firmware.', update_status=update_status)
if __name__ == '__main__':
main()

View file

@ -1,301 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 2.0
# Copyright (C) 2019 Dell Inc. or its subsidiaries. All Rights Reserved.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: idrac_server_config_profile
short_description: Export or Import iDRAC Server Configuration Profile (SCP).
description:
- Export the Server Configuration Profile (SCP) from the iDRAC or Import from a network share or a local file.
options:
idrac_ip:
description: iDRAC IP Address.
type: str
required: True
idrac_user:
description: iDRAC username.
type: str
required: True
idrac_password:
description: iDRAC user password.
type: str
required: True
aliases: ['idrac_pwd']
idrac_port:
description: iDRAC port.
type: int
default: 443
command:
description:
- If C(import), will perform SCP import operations.
- If C(export), will perform SCP export operations.
choices: ['import', 'export']
default: 'export'
job_wait:
description: Whether to wait for job completion or not.
type: bool
required: True
share_name:
description: CIFS or NFS Network Share or a local path.
type: str
required: True
share_user:
description: Network share user in the format 'user@domain' or 'domain\\user' if user is
part of a domain else 'user'. This option is mandatory for CIFS Network Share.
type: str
share_password:
description: Network share user password. This option is mandatory for CIFS Network Share.
type: str
aliases: ['share_pwd']
scp_file:
description: Server Configuration Profile file name. This option is mandatory for C(import) command.
type: str
scp_components:
description:
- If C(ALL), this module will import all components configurations from SCP file.
- If C(IDRAC), this module will import iDRAC configuration from SCP file.
- If C(BIOS), this module will import BIOS configuration from SCP file.
- If C(NIC), this module will import NIC configuration from SCP file.
- If C(RAID), this module will import RAID configuration from SCP file.
choices: ['ALL', 'IDRAC', 'BIOS', 'NIC', 'RAID']
default: 'ALL'
shutdown_type:
description:
- This option is applicable for C(import) command.
- If C(Graceful), it gracefully shuts down the server.
- If C(Forced), it forcefully shuts down the server.
- If C(NoReboot), it does not reboot the server.
choices: ['Graceful', 'Forced', 'NoReboot']
default: 'Graceful'
end_host_power_state:
description:
- This option is applicable for C(import) command.
- If C(On), End host power state is on.
- If C(Off), End host power state is off.
choices: ['On' ,'Off']
default: 'On'
export_format:
description: Specify the output file format. This option is applicable for C(export) command.
choices: ['JSON', 'XML']
default: 'XML'
export_use:
description: Specify the type of server configuration profile (SCP) to be exported.
This option is applicable for C(export) command.
choices: ['Default', 'Clone', 'Replace']
default: 'Default'
requirements:
- "omsdk"
- "python >= 2.7.5"
author: "Jagadeesh N V(@jagadeeshnv)"
'''
EXAMPLES = r'''
---
- name: Import Server Configuration Profile from a network share
community.general.idrac_server_config_profile:
idrac_ip: "192.168.0.1"
idrac_user: "user_name"
idrac_password: "user_password"
command: "import"
share_name: "192.168.0.2:/share"
share_user: "share_user_name"
share_password: "share_user_password"
scp_file: "scp_filename.xml"
scp_components: "ALL"
job_wait: True
- name: Import Server Configuration Profile from a local path
community.general.idrac_server_config_profile:
idrac_ip: "192.168.0.1"
idrac_user: "user_name"
idrac_password: "user_password"
command: "import"
share_name: "/scp_folder"
share_user: "share_user_name"
share_password: "share_user_password"
scp_file: "scp_filename.xml"
scp_components: "ALL"
job_wait: True
- name: Export Server Configuration Profile to a network share
community.general.idrac_server_config_profile:
idrac_ip: "192.168.0.1"
idrac_user: "user_name"
idrac_password: "user_password"
share_name: "192.168.0.2:/share"
share_user: "share_user_name"
share_password: "share_user_password"
job_wait: False
- name: Export Server Configuration Profile to a local path
community.general.idrac_server_config_profile:
idrac_ip: "192.168.0.1"
idrac_user: "user_name"
idrac_password: "user_password"
share_name: "/scp_folder"
share_user: "share_user_name"
share_password: "share_user_password"
job_wait: False
'''
RETURN = r'''
---
msg:
type: str
description: Status of the import or export SCP job.
returned: always
sample: "Successfully imported the Server Configuration Profile"
scp_status:
type: dict
description: SCP operation job and progress details from the iDRAC.
returned: success
sample:
{
"Id": "JID_XXXXXXXXX",
"JobState": "Completed",
"JobType": "ImportConfiguration",
"Message": "Successfully imported and applied Server Configuration Profile.",
"MessageArgs": [],
"MessageId": "XXX123",
"Name": "Import Configuration",
"PercentComplete": 100,
"StartTime": "TIME_NOW",
"Status": "Success",
"TargetSettingsURI": null,
"retval": true
}
'''
import os
from ansible_collections.community.general.plugins.module_utils.remote_management.dellemc.dellemc_idrac import iDRACConnection
from ansible.module_utils.basic import AnsibleModule
try:
from omsdk.sdkfile import file_share_manager
from omsdk.sdkcreds import UserCredentials
from omdrivers.enums.iDRAC.iDRACEnums import (SCPTargetEnum, EndHostPowerStateEnum,
ShutdownTypeEnum, ExportFormatEnum, ExportUseEnum)
except ImportError:
pass
def run_import_server_config_profile(idrac, module):
"""Import Server Configuration Profile from a network share."""
target = SCPTargetEnum[module.params['scp_components']]
job_wait = module.params['job_wait']
end_host_power_state = EndHostPowerStateEnum[module.params['end_host_power_state']]
shutdown_type = ShutdownTypeEnum[module.params['shutdown_type']]
idrac.use_redfish = True
try:
myshare = file_share_manager.create_share_obj(
share_path="{0}{1}{2}".format(module.params['share_name'], os.sep, module.params['scp_file']),
creds=UserCredentials(module.params['share_user'],
module.params['share_password']), isFolder=False)
import_status = idrac.config_mgr.scp_import(myshare,
target=target, shutdown_type=shutdown_type,
end_host_power_state=end_host_power_state,
job_wait=job_wait)
if not import_status or import_status.get('Status') != "Success":
module.fail_json(msg='Failed to import scp.', scp_status=import_status)
except RuntimeError as e:
module.fail_json(msg=str(e))
return import_status
def run_export_server_config_profile(idrac, module):
"""Export Server Configuration Profile to a network share."""
export_format = ExportFormatEnum[module.params['export_format']]
scp_file_name_format = "%ip_%Y%m%d_%H%M%S_scp.{0}".format(module.params['export_format'].lower())
target = SCPTargetEnum[module.params['scp_components']]
export_use = ExportUseEnum[module.params['export_use']]
idrac.use_redfish = True
try:
myshare = file_share_manager.create_share_obj(share_path=module.params['share_name'],
creds=UserCredentials(module.params['share_user'],
module.params['share_password']),
isFolder=True)
scp_file_name = myshare.new_file(scp_file_name_format)
export_status = idrac.config_mgr.scp_export(scp_file_name,
target=target,
export_format=export_format,
export_use=export_use,
job_wait=module.params['job_wait'])
if not export_status or export_status.get('Status') != "Success":
module.fail_json(msg='Failed to export scp.', scp_status=export_status)
except RuntimeError as e:
module.fail_json(msg=str(e))
return export_status
def main():
module = AnsibleModule(
argument_spec={
"idrac_ip": {"required": True, "type": 'str'},
"idrac_user": {"required": True, "type": 'str'},
"idrac_password": {"required": True, "type": 'str',
"aliases": ['idrac_pwd'], "no_log": True},
"idrac_port": {"required": False, "default": 443, "type": 'int'},
"command": {"required": False, "type": 'str',
"choices": ['export', 'import'], "default": 'export'},
"job_wait": {"required": True, "type": 'bool'},
"share_name": {"required": True, "type": 'str'},
"share_user": {"required": False, "type": 'str'},
"share_password": {"required": False, "type": 'str',
"aliases": ['share_pwd'], "no_log": True},
"scp_components": {"required": False,
"choices": ['ALL', 'IDRAC', 'BIOS', 'NIC', 'RAID'],
"default": 'ALL'},
"scp_file": {"required": False, "type": 'str'},
"shutdown_type": {"required": False,
"choices": ['Graceful', 'Forced', 'NoReboot'],
"default": 'Graceful'},
"end_host_power_state": {"required": False,
"choices": ['On', 'Off'],
"default": 'On'},
"export_format": {"required": False, "type": 'str',
"choices": ['JSON', 'XML'], "default": 'XML'},
"export_use": {"required": False, "type": 'str',
"choices": ['Default', 'Clone', 'Replace'], "default": 'Default'}
},
required_if=[
["command", "import", ["scp_file"]]
],
supports_check_mode=False)
try:
changed = False
with iDRACConnection(module.params) as idrac:
command = module.params['command']
if command == 'import':
scp_status = run_import_server_config_profile(idrac, module)
if "No changes were applied" not in scp_status.get('Message', ""):
changed = True
else:
scp_status = run_export_server_config_profile(idrac, module)
module.exit_json(changed=changed, msg="Successfully {0}ed the Server Configuration Profile.".format(command),
scp_status=scp_status)
except (ImportError, ValueError, RuntimeError) as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View file

@ -1,413 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 1.2
# Copyright (C) 2019 Dell Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# All rights reserved. Dell, EMC, and other trademarks are trademarks of Dell Inc. or its subsidiaries.
# Other trademarks may be trademarks of their respective owners.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: ome_device_info
short_description: Retrieves the information about Device.
description:
- This module retrieves the list of all devices information with the exhaustive inventory of each
device.
options:
hostname:
description:
- Target IP Address or hostname.
type: str
required: True
username:
description:
- Target username.
type: str
required: True
password:
description:
- Target user password.
type: str
required: True
port:
description:
- Target HTTPS port.
type: int
default: 443
fact_subset:
description:
- C(basic_inventory) returns the list of the devices.
- C(detailed_inventory) returns the inventory details of specified devices.
- C(subsystem_health) returns the health status of specified devices.
type: str
choices: [basic_inventory, detailed_inventory, subsystem_health ]
default: basic_inventory
system_query_options:
description:
- I(system_query_options) applicable for the choices of the fact_subset. Either I(device_id) or I(device_service_tag)
is mandatory for C(detailed_inventory) and C(subsystem_health) or both can be applicable.
type: dict
suboptions:
device_id:
description:
- A list of unique identifier is applicable
for C(detailed_inventory) and C(subsystem_health).
type: list
device_service_tag:
description:
- A list of service tags are applicable for C(detailed_inventory)
and C(subsystem_health).
type: list
inventory_type:
description:
- For C(detailed_inventory), it returns details of the specified inventory type.
type: str
filter:
description:
- For C(basic_inventory), it filters the collection of devices.
I(filter) query format should be aligned with OData standards.
type: str
requirements:
- "python >= 2.7.5"
author: "Sajna Shetty(@Sajna-Shetty)"
'''
EXAMPLES = """
---
- name: Retrieve basic inventory of all devices.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
- name: Retrieve basic inventory for devices identified by IDs 33333 or 11111 using filtering.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
fact_subset: "basic_inventory"
system_query_options:
filter: "Id eq 33333 or Id eq 11111"
- name: Retrieve inventory details of specified devices identified by IDs 11111 and 22222.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
fact_subset: "detailed_inventory"
system_query_options:
device_id:
- 11111
- 22222
- name: Retrieve inventory details of specified devices identified by service tags MXL1234 and MXL4567.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
fact_subset: "detailed_inventory"
system_query_options:
device_service_tag:
- MXL1234
- MXL4567
- name: Retrieve details of specified inventory type of specified devices identified by ID and service tags.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
fact_subset: "detailed_inventory"
system_query_options:
device_id:
- 11111
device_service_tag:
- MXL1234
- MXL4567
inventory_type: "serverDeviceCards"
- name: Retrieve subsystem health of specified devices identified by service tags.
community.general.ome_device_info:
hostname: "192.168.0.1"
username: "username"
password: "password"
fact_subset: "subsystem_health"
system_query_options:
device_service_tag:
- MXL1234
- MXL4567
"""
RETURN = '''
---
msg:
type: str
description: Over all device information status.
returned: on error
sample: "Failed to fetch the device information"
device_info:
type: dict
description: Returns the information collected from the Device.
returned: success
sample: {
"value": [
{
"Actions": null,
"AssetTag": null,
"ChassisServiceTag": null,
"ConnectionState": true,
"DeviceManagement": [
{
"DnsName": "dnsname.host.com",
"InstrumentationName": "MX-12345",
"MacAddress": "11:10:11:10:11:10",
"ManagementId": 12345,
"ManagementProfile": [
{
"HasCreds": 0,
"ManagementId": 12345,
"ManagementProfileId": 12345,
"ManagementURL": "https://192.168.0.1:443",
"Status": 1000,
"StatusDateTime": "2019-01-21 06:30:08.501"
}
],
"ManagementType": 2,
"NetworkAddress": "192.168.0.1"
}
],
"DeviceName": "MX-0003I",
"DeviceServiceTag": "MXL1234",
"DeviceSubscription": null,
"LastInventoryTime": "2019-01-21 06:30:08.501",
"LastStatusTime": "2019-01-21 06:30:02.492",
"ManagedState": 3000,
"Model": "PowerEdge MX7000",
"PowerState": 17,
"SlotConfiguration": {},
"Status": 4000,
"SystemId": 2031,
"Type": 2000
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.remote_management.dellemc.ome import RestOME
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
from ansible.module_utils.urls import ConnectionError, SSLValidationError
DEVICES_INVENTORY_DETAILS = "detailed_inventory"
DEVICES_SUBSYSTEM_HEALTH = "subsystem_health"
DEVICES_INVENTORY_TYPE = "inventory_type"
DEVICE_LIST = "basic_inventory"
DESC_HTTP_ERROR = "HTTP Error 404: Not Found"
device_fact_error_report = {}
DEVICE_RESOURCE_COLLECTION = {
DEVICE_LIST: {"resource": "DeviceService/Devices"},
DEVICES_INVENTORY_DETAILS: {"resource": "DeviceService/Devices({Id})/InventoryDetails"},
DEVICES_INVENTORY_TYPE: {"resource": "DeviceService/Devices({Id})/InventoryDetails('{InventoryType}')"},
DEVICES_SUBSYSTEM_HEALTH: {"resource": "DeviceService/Devices({Id})/SubSystemHealth"},
}
def _get_device_id_from_service_tags(service_tags, rest_obj):
"""
Get device ids from device service tag
Returns :dict : device_id to service_tag map
:arg service_tags: service tag
:arg rest_obj: RestOME class object in case of request with session.
:returns: dict eg: {1345:"MXL1245"}
"""
try:
path = DEVICE_RESOURCE_COLLECTION[DEVICE_LIST]["resource"]
resp = rest_obj.invoke_request('GET', path)
if resp.success:
devices_list = resp.json_data["value"]
service_tag_dict = {}
for item in devices_list:
if item["DeviceServiceTag"] in service_tags:
service_tag_dict.update({item["Id"]: item["DeviceServiceTag"]})
available_service_tags = service_tag_dict.values()
not_available_service_tag = list(set(service_tags) - set(available_service_tags))
device_fact_error_report.update(dict((tag, DESC_HTTP_ERROR) for tag in not_available_service_tag))
else:
raise ValueError(resp.json_data)
except (URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError) as err:
raise err
return service_tag_dict
def is_int(val):
"""check when device_id numeric represented value is int"""
try:
int(val)
return True
except ValueError:
return False
def _check_duplicate_device_id(device_id_list, service_tag_dict):
"""If service_tag is duplicate of device_id, then updates the message as Duplicate report
:arg1: device_id_list : list of device_id
:arg2: service_tag_id_dict: dictionary of device_id to service tag map"""
if device_id_list:
device_id_represents_int = [int(device_id) for device_id in device_id_list if device_id and is_int(device_id)]
common_val = list(set(device_id_represents_int) & set(service_tag_dict.keys()))
for device_id in common_val:
device_fact_error_report.update(
{service_tag_dict[device_id]: "Duplicate report of device_id: {0}".format(device_id)})
del service_tag_dict[device_id]
def _get_device_identifier_map(module_params, rest_obj):
"""
Builds the identifiers mapping
:returns: the dict of device_id to server_tag map
eg: {"device_id":{1234: None},"device_service_tag":{1345:"MXL1234"}}"""
system_query_options_param = module_params.get("system_query_options")
device_id_service_tag_dict = {}
if system_query_options_param is not None:
device_id_list = system_query_options_param.get("device_id")
device_service_tag_list = system_query_options_param.get("device_service_tag")
if device_id_list:
device_id_dict = dict((device_id, None) for device_id in list(set(device_id_list)))
device_id_service_tag_dict["device_id"] = device_id_dict
if device_service_tag_list:
service_tag_dict = _get_device_id_from_service_tags(device_service_tag_list,
rest_obj)
_check_duplicate_device_id(device_id_list, service_tag_dict)
device_id_service_tag_dict["device_service_tag"] = service_tag_dict
return device_id_service_tag_dict
def _get_query_parameters(module_params):
"""
Builds query parameter
:returns: dictionary, which is applicable builds the query format
eg : {"$filter":"Type eq 2000"}
"""
system_query_options_param = module_params.get("system_query_options")
query_parameter = None
if system_query_options_param:
filter_by_val = system_query_options_param.get("filter")
if filter_by_val:
query_parameter = {"$filter": filter_by_val}
return query_parameter
def _get_resource_parameters(module_params, rest_obj):
"""
Identifies the resource path by different states
:returns: dictionary containing identifier with respective resource path
eg:{"device_id":{1234:""DeviceService/Devices(1234)/InventoryDetails"},
"device_service_tag":{"MXL1234":"DeviceService/Devices(1345)/InventoryDetails"}}
"""
fact_subset = module_params["fact_subset"]
path_dict = {}
if fact_subset != DEVICE_LIST:
inventory_type = None
device_id_service_tag_dict = _get_device_identifier_map(module_params, rest_obj)
if fact_subset == DEVICES_INVENTORY_DETAILS:
system_query_options = module_params.get("system_query_options")
inventory_type = system_query_options.get(DEVICES_INVENTORY_TYPE)
path_identifier = DEVICES_INVENTORY_TYPE if inventory_type else fact_subset
for identifier_type, identifier_dict in device_id_service_tag_dict.items():
path_dict[identifier_type] = {}
for device_id, service_tag in identifier_dict.items():
key_identifier = service_tag if identifier_type == "device_service_tag" else device_id
path = DEVICE_RESOURCE_COLLECTION[path_identifier]["resource"].format(Id=device_id,
InventoryType=inventory_type)
path_dict[identifier_type].update({key_identifier: path})
else:
path_dict.update({DEVICE_LIST: DEVICE_RESOURCE_COLLECTION[DEVICE_LIST]["resource"]})
return path_dict
def _check_mutually_inclusive_arguments(val, module_params, required_args):
""""
Throws error if arguments detailed_inventory, subsystem_health
not exists with qualifier device_id or device_service_tag"""
system_query_options_param = module_params.get("system_query_options")
if system_query_options_param is None or (system_query_options_param is not None and not any(
system_query_options_param.get(qualifier) for qualifier in required_args)):
raise ValueError("One of the following {0} is required for {1}".format(required_args, val))
def _validate_inputs(module_params):
"""validates input parameters"""
fact_subset = module_params["fact_subset"]
if fact_subset != "basic_inventory":
_check_mutually_inclusive_arguments(fact_subset, module_params, ["device_id", "device_service_tag"])
def main():
system_query_options = {"type": 'dict', "required": False, "options": {
"device_id": {"type": 'list'},
"device_service_tag": {"type": 'list'},
"inventory_type": {"type": 'str'},
"filter": {"type": 'str', "required": False},
}}
module = AnsibleModule(
argument_spec={
"hostname": {"required": True, "type": 'str'},
"username": {"required": True, "type": 'str'},
"password": {"required": True, "type": 'str', "no_log": True},
"port": {"required": False, "default": 443, "type": 'int'},
"fact_subset": {"required": False, "default": "basic_inventory",
"choices": ['basic_inventory', 'detailed_inventory', 'subsystem_health']},
"system_query_options": system_query_options,
},
required_if=[['fact_subset', 'detailed_inventory', ['system_query_options']],
['fact_subset', 'subsystem_health', ['system_query_options']], ],
supports_check_mode=False)
try:
_validate_inputs(module.params)
with RestOME(module.params, req_session=True) as rest_obj:
device_facts = _get_resource_parameters(module.params, rest_obj)
resp_status = []
if device_facts.get("basic_inventory"):
query_param = _get_query_parameters(module.params)
resp = rest_obj.invoke_request('GET', device_facts["basic_inventory"], query_param=query_param)
device_facts = resp.json_data
resp_status.append(resp.status_code)
else:
for identifier_type, path_dict_map in device_facts.items():
for identifier, path in path_dict_map.items():
try:
resp = rest_obj.invoke_request('GET', path)
data = resp.json_data
resp_status.append(resp.status_code)
except HTTPError as err:
data = str(err)
path_dict_map[identifier] = data
if any(device_fact_error_report):
if "device_service_tag" in device_facts:
device_facts["device_service_tag"].update(device_fact_error_report)
else:
device_facts["device_service_tag"] = device_fact_error_report
if 200 in resp_status:
module.exit_json(device_info=device_facts)
else:
module.fail_json(msg="Failed to fetch the device information")
except (URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError) as err:
module.fail_json(msg=str(err))
if __name__ == '__main__':
main()