From 8a9d836267d2d9eddd38ed695db0385b031bf59c Mon Sep 17 00:00:00 2001
From: akumch <37585199+akumch@users.noreply.github.com>
Date: Thu, 22 Nov 2018 07:41:26 +0100
Subject: [PATCH] Ansible Integration V1.2
---
DOCUMENTATION.md | 1279 ++++++++++++++---
LICENSE.md | 2 -
README.md | 54 +-
examples/boot_to_virtual_cd.yml | 3 +-
...r_configuration_against_saved_profiles.yml | 80 ++
.../create_new_user_and_remove_old_user.yml | 3 +-
examples/create_user_from_file.yml | 5 +-
examples/export_ldap_settings_to_file.yml | 2 +-
...server_configuration_profiles_to_files.yml | 66 +
examples/export_user_data_to_file.yml | 2 +-
examples/get_server_facts.yml | 3 +-
examples/import_ldap_settings_from_file.yml | 3 +-
examples/irmc_biosbootorder_examples.yml | 46 +
examples/irmc_cas_examples.yml | 50 +
examples/irmc_certificate_examples.yml | 3 +-
examples/irmc_compare_profiles_examples.yml | 31 +
examples/irmc_connectvm_examples.yml | 3 +-
examples/irmc_eventlog_examples.yml | 46 +
examples/irmc_facts_examples.yml | 3 +-
examples/irmc_fwbios_update_examples.yml | 67 +
examples/irmc_getvm_examples.yml | 3 +-
examples/irmc_idled_examples.yml | 3 +-
examples/irmc_ldap_examples.yml | 3 +-
examples/irmc_license_examples.yml | 3 +-
examples/irmc_ntp_examples.yml | 49 +
examples/irmc_powerstate_examples.yml | 3 +-
examples/irmc_profiles_examples.yml | 45 +
examples/irmc_scci_examples.yml | 3 +-
examples/irmc_session_examples.yml | 45 +
examples/irmc_setnextboot_examples.yml | 3 +-
examples/irmc_setvm_examples.yml | 3 +-
examples/irmc_task_examples.yml | 45 +
examples/irmc_user_examples.yml | 3 +-
examples/set_bios_boot_order.yml | 46 +
examples/set_bios_boot_order_default.yml | 39 +
group_vars/all | 11 +
library/irmc_biosbootorder.py | 431 ++++++
library/irmc_cas.py | 336 +++++
library/irmc_certificate.py | 108 +-
library/irmc_compare_profiles.py | 156 ++
library/irmc_connectvm.py | 36 +-
library/irmc_eventlog.py | 255 ++++
library/irmc_facts.py | 420 +++++-
library/irmc_fwbios_update.py | 473 ++++++
library/irmc_getvm.py | 90 +-
library/irmc_idled.py | 43 +-
library/irmc_ldap.py | 241 +++-
library/irmc_license.py | 46 +-
library/irmc_ntp.py | 240 ++++
library/irmc_powerstate.py | 53 +-
library/irmc_profiles.py | 351 +++++
library/irmc_scci.py | 84 +-
library/irmc_session.py | 294 ++++
library/irmc_setnextboot.py | 37 +-
library/irmc_setvm.py | 55 +-
library/irmc_task.py | 224 +++
library/irmc_user.py | 315 +++-
module_utils/irmc.py | 68 +-
module_utils/irmc_scci_utils.py | 85 +-
module_utils/irmc_upload_file.py | 65 +
module_utils/irmc_utils.py | 110 ++
tests/test_irmc.py | 48 +-
tests/test_irmc_scci_utils.py | 84 +-
tests/test_irmc_upload_file.py | 118 ++
tests/test_irmc_utils.py | 215 +++
65 files changed, 6327 insertions(+), 812 deletions(-)
create mode 100644 examples/compare_server_configuration_against_saved_profiles.yml
create mode 100644 examples/export_server_configuration_profiles_to_files.yml
create mode 100644 examples/irmc_biosbootorder_examples.yml
create mode 100644 examples/irmc_cas_examples.yml
create mode 100644 examples/irmc_compare_profiles_examples.yml
create mode 100644 examples/irmc_eventlog_examples.yml
create mode 100644 examples/irmc_fwbios_update_examples.yml
create mode 100644 examples/irmc_ntp_examples.yml
create mode 100644 examples/irmc_profiles_examples.yml
create mode 100644 examples/irmc_session_examples.yml
create mode 100644 examples/irmc_task_examples.yml
create mode 100644 examples/set_bios_boot_order.yml
create mode 100644 examples/set_bios_boot_order_default.yml
create mode 100644 library/irmc_biosbootorder.py
create mode 100644 library/irmc_cas.py
create mode 100644 library/irmc_compare_profiles.py
create mode 100644 library/irmc_eventlog.py
create mode 100644 library/irmc_fwbios_update.py
create mode 100644 library/irmc_ntp.py
create mode 100644 library/irmc_profiles.py
create mode 100644 library/irmc_session.py
create mode 100644 library/irmc_task.py
create mode 100644 module_utils/irmc_upload_file.py
create mode 100644 module_utils/irmc_utils.py
create mode 100644 tests/test_irmc_upload_file.py
create mode 100644 tests/test_irmc_utils.py
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index 2a3fe17..950c6e6 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -2,43 +2,222 @@
## Modules
+ * [irmc_biosbootorder - configure iRMC to force next boot to specified option](#irmc_biosbootorder)
+ * [irmc_cas - manage iRMC CAS settings](#irmc_cas)
* [irmc_certificate - manage iRMC certificates](#irmc_certificate)
+ * [irmc_compare_profiles - compare two iRMC profiles](#irmc_compare_profiles)
* [irmc_connectvm - connect iRMC Virtual Media Data](#irmc_connectvm)
+ * [irmc_eventlog - handle iRMC eventlogs](#irmc_eventlog)
* [irmc_facts - get or set PRIMERGY server and iRMC facts](#irmc_facts)
+ * [irmc_fwbios_update - update iRMC Firmware or server BIOS](#irmc_fwbios_update)
* [irmc_getvm - get iRMC Virtual Media Data](#irmc_getvm)
* [irmc_idled - get or set server ID LED](#irmc_idled)
* [irmc_ldap - manage iRMC LDAP settings](#irmc_ldap)
* [irmc_license - manage iRMC user accounts](#irmc_license)
+ * [irmc_ntp - manage iRMC time options](#irmc_ntp)
* [irmc_powerstate - get or set server power state](#irmc_powerstate)
+ * [irmc_profiles - handle iRMC profiles](#irmc_profiles)
* [irmc_scci - execute iRMC remote SCCI commands](#irmc_scci)
+ * [irmc_session - handle iRMC sessions](#irmc_session)
* [irmc_setnextboot - configure iRMC to force next boot to specified option](#irmc_setnextboot)
* [irmc_setvm - set iRMC Virtual Media Data](#irmc_setvm)
+ * [irmc_task - handle iRMC tasks](#irmc_task)
* [irmc_user - manage iRMC user accounts](#irmc_user)
---
-### irmc_certificate
+### irmc_biosbootorder
+
+#### Description
+* Ansible module to configure the BIOS boot oder via iRMC.
+* Using this module will force server into several reboots.
+* The module will abort by default if the PRIMERGY server is powered on.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * The PRIMERGY server needs to be at least a M2 model.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| boot_device | No | | | String to match with specified key for existing boot devices. Needs to be provided for 'set' command. |
+| boot_key | No | StructuredBootString | DeviceName
StructuredBootString
| Which key to check for in bios boot order devices. |
+| command | No | get | get
set
default
| Get or set BIOS Boot Order. |
+| force_new | No | False | | Force generation of new BiosBootOrder configuration in iRMC before getting or setting boot order. |
+| ignore_power_on | No | False | | Ignore that server is powered on. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| next_boot_device | No | | | Set next boot to specified device. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+
+#### Examples
+```yaml
+# Get Bios Boot Order
+- name: Get Bios Boot Order
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ force_new: false
+ delegate_to: localhost
+
+# Set Bios Boot Order to default
+- name: Get Bios Boot Order to default
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "default"
+ ignore_power_on: false
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**boot_order returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| DeviceIdx | device index | always | int | 1 |
+| DeviceName | device name | always | string | (Bus 01 Dev 00)PCI RAID Adapter |
+| StructuredBootString | structured boot string | always | string | RAID.Slot.1.Legacy |
+
+**For all other commands:**
+
+Default return values
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+
+---
+### irmc_cas
+
+#### Description
+* Ansible module to manage iRMC CAS settings via iRMC remote scripting interface.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | get | get
set
| How to handle iRMC CAS data. |
+| enabled | No | | | CAS enabled. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| login_always | No | | | Always Display Login Page. |
+| login_uri | No | | | CAS Login URL. |
+| logout_uri | No | | | CAS Logout URL. |
+| port | No | | | CAS Port. |
+| privilege_avr | No | | | Video Redirection Enabled. |
+| privilege_bmc | No | | | Configure iRMC Settings. |
+| privilege_level | No | | Reserved
Callback
User
Operator
Administrator
OEM
NoAccess
| Privilege Level. |
+| privilege_source | No | | Local
LDAP
| Assign CAS permissions from. |
+| privilege_storage | No | | | Remote Storage Enable. |
+| privilege_user | No | | | Configure User Accounts. |
+| server | No | | | CAS Server. |
+| ssl_verify | No | | | Verify SSL Certificate. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+| validate_uri | No | | | CAS Validate URL. |
+
+#### Examples
+```yaml
+# Get CAS data
+- name: Get CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: cas
+ delegate_to: localhost
+- name: Show iRMC CAS data
+ debug:
+ msg: "{{ cas.cas }}"
+
+# Set CAS data
+- name: Set CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ cas_user: "username"
+ cas_password: "password"
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**CAS data returned by command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| enabled | CAS enabled | always | bool | False |
+| login_always | always Display Login Page | always | bool | True |
+| login_uri | CAS Login URL | always | string | /cas/login |
+| logout_uri | CAS Logout URL | always | string | /cas/logout |
+| port | CAS port | always | int | 3170 |
+| privilege_avr | Video Redirection Enabled | always | bool | False |
+| privilege_bmc | configure iRMC Settings | always | bool | False |
+| privilege_level | privilege Level | always | string | Operator |
+| privilege_source | assign CAS permissions from | always | string | Local |
+| privilege_storage | Remote Storage Enable | always | bool | False |
+| privilege_user | configure User Accounts | always | bool | False |
+| server | CAS server | always | string | cas_server.local |
+| ssl_verify | verify SSL Certificate | always | bool | True |
+| validate_uri | CAS Validate URL | always | string | /cas/validate |
+
+**For command "set":**
+
+Default return values
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/12563/wp-svs-irmc-remote-scripting-en.pdf
+- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
+
+---
+### irmc_certificate
#### Description
* Ansible module to manage iRMC certificates via iRMC remote scripting interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| command | No | get |
| get or set iRMC certificate(s) |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| private_key_path | No | | | path to file containing SSL private key; this option also requires the SSL certificate |
-| ssl_ca_cert_path | No | | | path to file containing SSL CA certificate |
-| ssl_cert_path | No | | | path to file containing SSL certificate; this option also requires the SSL private key |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| command | No | get | get
set
| Get or set iRMC certificate(s). |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| private_key_path | No | | | Path to file containing SSL private key. This option also requires the SSL certificate. |
+| ssl_ca_cert_path | No | | | Path to file containing SSL CA certificate. |
+| ssl_cert_path | No | | | Path to file containing SSL certificate. This option also requires the SSL private key. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -72,9 +251,16 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| certificates | SSL certificates | always | dict |
+**Certificates returned by command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| ssl_ca_certificate | SSL CA certificate | always | string | |
+| ssl_certificate | SSL certificate | always | string | |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -82,27 +268,67 @@
- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
---
-### irmc_connectvm
+### irmc_compare_profiles
+
+#### Description
+* Ansible module to compare two iRMC profiles.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| profile_json1 | No | | | iRMC profile to be compared against another. Takes precedence over profile_path1 when set. |
+| profile_json2 | No | | | iRMC profile to be compared against another. Takes precedence over profile_path2 when set. |
+| profile_path1 | No | | | Path to file with iRMC profile to be compared against another. Ignored if profile1 is set. |
+| profile_path2 | No | | | Path to file with iRMC profile to be compared against another. Ignored if profile2 is set. |
+
+#### Examples
+```yaml
+# Compare iRMC profiles against each other
+- name: Compare iRMC profiles
+ irmc_compare_profiles:
+ profile_path1: "{{ profile1_path }}"
+ profile_path2: "{{ profile2_path }}"
+ delegate_to: localhost
+```
+
+#### Return Values
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| comparison_list | rudimentary list of probable comparison differences | when comparison_result is False | list | |
+| comparison_result | profile comparison result | always | bool | False |
+---
+### irmc_connectvm
+
#### Description
* Ansible module to connect iRMC Virtual Media Data via the iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| command | No | ConnectCD | - ConnectCD
- ConnectFD
- ConnectHD
- DisconnectCD
- DisconnectFD
- DisconnectHD
| the virtual media connect command to be executed |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| command | No | ConnectCD | ConnectCD
ConnectFD
ConnectHD
DisconnectCD
DisconnectFD
DisconnectHD
| The virtual media connect command to be executed. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -129,9 +355,7 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| result | connection action result | always | dict |
+Default return values
#### Notes
@@ -139,31 +363,111 @@
- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
---
-### irmc_facts
+### irmc_eventlog
+
+#### Description
+* Ansible module to handle iRMC eventlogs via Restful API.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | list | list
get
clear
| Handle iRMC eventlogs. |
+| eventlog_type | No | SystemEventLog | SystemEventLog
InternalEventLog
| Specific eventlog to handle. |
+| id | No | | | Specific eventlog ID to get. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+
+#### Examples
+```yaml
+# List iRMC InternalEventLog
+- name: List iRMC InternalEventLog
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ eventlog_type: "InternalEventLog"
+ delegate_to: localhost
+
+# Get specific SystemEventLog entry information
+- name: Get specific SystemEventLog entry information
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 0
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**eventlog_entry data returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| AlertGroup | group which caused the event | always | string | Memory |
+| Cause | reason for the event if available | always | string | The memory module was not approved and released for this system by the system manufacturer |
+| Created | dated of the event | always | string | 2018-07-24T15:57:40+02:00 |
+| EventSource | where the event originated from | always | string | iRMC S5 |
+| Id | event ID | always | int | 20 |
+| Message | event entry text | always | string | DIMM-1E Non Fujitsu Memory Module detected - Warranty restricted! |
+| Resolutions | list of possible solitions for the problem, if available | always | list | |
+| Severity | serverity of event | always | string | Warning |
+| Type | event type | always | string | SEL |
+
+**eventlog data returned for command "list":**
+
+List of individual eventlog_entries (see above)
+
+**For all other commands:**
+
+Default return values
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+---
+### irmc_facts
#### Description
* Ansible module to get or set basic iRMC and PRIMERGY server data via iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| asset_tag | No | | | server asset tag |
-| command | No | get | | get or set server facts |
-| description | No | | | server description |
-| helpdesk_message | No | | | help desk message |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| location | No | | | server location |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| asset_tag | No | | | Server asset tag. |
+| command | No | get | get
set
| Get or set server facts. |
+| contact | No | | | System contact. |
+| description | No | | | Server description. |
+| helpdesk_message | No | | | Help desk message. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| location | No | | | Server location. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -195,9 +499,23 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| facts | basic server and iRMC facts | always | dict |
+**facts returned by command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| hardware.ethernetinterfaces | dict with total number (count) and list of ethernet interfaces (devices) with relevant data (id, macaddress, name) | always | dict | {u'count': 2, u'devices': [{u'macaddress': u'01:02:03:04:05:06', u'id': u'0', u'name': u'eth0'}, {u'macaddress': u'01:02:03:04:05:06', u'id': u'1', u'name': u'eth1'}]} |
+| hardware.fans | dict with available fan slots (sockets) and total number (count) and list of existing fans (devices) with relevant data (id, manufacturer, name, size)
note that fan devices are only returned if server is 'On' | always | dict | {u'count': 2, u'sockets': 24, u'devices': [{u'manufacturer': u'Micron Technology', u'id': u'0', u'name': u'DIMM-1A', u'size': 8192}, {u'manufacturer': u'SK Hynix', u'id': u'12', u'name': u'DIMM-1E', u'size': 16384}]} |
+| hardware.memory | dict with available memory slots (sockets) and total number (count) and list of existing memories (devices) with relevant data (id, manufacturer, name, size) | always | dict | {u'count': 6, u'sockets': 7, u'devices': [{u'location': u'SystemBoard', u'id': u'0', u'name': u'FAN1 SYS'}, {u'location': u'SystemBoard', u'id': u'1', u'name': u'FAN2 SYS'}, {u'location': u'SystemBoard', u'id': u'2', u'name': u'FAN3 SYS'}, {u'location': u'SystemBoard', u'id': u'3', u'name': u'FAN4 SYS'}, {u'location': u'SystemBoard', u'id': u'4', u'name': u'FAN5 SYS'}, {u'location': u'PowerSupply', u'id': u'5', u'name': u'FAN PSU1'}]} |
+| hardware.powersupplies | dict with available power supply slots (sockets) and total number (count) and list of existing power supplies (devices) with relevant data (id, manufacturer, model, name) | always | dict | {u'count': 1, u'sockets': 2, u'devices': [{u'model': u'S13-450P1A', u'manufacturer': u'CHICONY', u'id': u'0', u'name': u'PSU1'}]} |
+| hardware.processors | dict with available processor slots (sockets) and total number (count) and list of existing processors (devices) with relevant data (cores, id, name, threads) | always | dict | {u'count': 2, u'sockets': 2, u'devices': [{u'cores': 6, u'threads': 6, u'id': u'0', u'name': u'Genuine Intel(R) CPU @ 2.00GHz'}, {u'cores': 6, u'threads': 6, u'id': u'1', u'name': u'Genuine Intel(R) CPU @ 2.00GHz'}]} |
+| hardware.storagecontrollers | dict with total number (count) and list of storage controllers (devices) with relevant data (drives, firmware, id, name, volume)
note that storage controllers are only returned if server is 'On' | always | dict | {u'count': 1, u'devices': [{u'name': u'PRAID EP400i', u'firmware': u'4.270.00-4869', u'id': u'1000', u'volumes': 1, u'drives': 4}]} |
+| irmc | dict with relevant iRMC data (fw_builddate, fw_running, fw_version, hostname, macaddress, sdrr_version) | always | dict | {u'macaddress': u'90:1B:0E:01:CA:5C', u'hostname': u'iRMC01CA5C-iRMC', u'fw_version': u'9.08F', u'fw_running': u'LowFWImage', u'fw_builddate': u'2018-03-05T14:02:44', u'sdrr_version': u'3.73'} |
+| mainboard | dict with relevant mainboard data (dnumber, manufacturer, part_number, serial_number, version) | always | dict | {u'dnumber': u'D3289', u'serial_number': u'44617895', u'part_number': u'S26361-D3289-D13', u'version': u'WGS04 GS50', u'manufacturer': u'FUJITSU'} |
+| system | dict with relevant system data (asset_tag, bios_version, description, health, helpdesk_message, host_name, idled_state, ip, location, manufacturer, memory_size, model, part_number, power_state, serial_number, uuid) | always | dict | {u'uuid': u'11223344-5566-cafe-babe-deadbeef1234', u'helpdesk_message': u'New helpdesk message', u'ip': u'101.102.103.104', u'description': u'server description', u'asset_tag': u'New AssetTag', u'part_number': u'ABN:K1495-VXXX-XX', u'contact': u'Admin (admin@server.room)', u'memory_size': u'24 GB', u'host_name': u'STK-SLES11SP4x64', u'power_state': u'On', u'bios_version': u'V5.0.0.9 R1.36.0 for D3289-A1x', u'serial_number': u'YLVT000098', u'model': u'PRIMERGY RX2540 M1', u'manufacturer': u'FUJITSU', u'health': u'OK', u'idled_state': u'Off', u'location': u'Server Room'} |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -205,27 +523,135 @@
- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
---
-### irmc_getvm
+### irmc_fwbios_update
+
+#### Description
+* Ansible module to get current iRMC update settings or update iRMC Firmware or BIOS via iRMC RedFish interface.
+* BIOS or firmware flash can be initiated from TFTP server or local file.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+ * python module 'requests_toolbelt'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | get | get
update
| Get settings or run update. |
+| file_name | No | | | Path to file containing correct iRMC FW or server BIOS image. |
+| ignore_power_on | No | False | | Ignore that server is powered on. |
+| irmc_boot_selector | No | | Auto
LowFWImage
HighFWImage
| Which iRMC FW image is to be started after iRMC reboot. |
+| irmc_flash_selector | No | | Auto
LowFWImage
HighFWImage
| Which iRMC image to replace with the new firmware. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| server_name | No | | | TFTP server name or IP. ignored if update_source is set to 'file' |
+| update_source | No | | tftp
file
| Where to get the FW or BIOS update file. |
+| update_type | No | | irmc
bios
| Whether to update iRMC FW or server BIOS. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+
+#### Examples
+```yaml
+# Get irmc firmware and BIOS update settings
+- name: Get irmc firmware and BIOS update settings
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: fw_settings
+ delegate_to: localhost
+- name: Show irmc firmware and BIOS update settings
+ debug:
+ msg: "{{ fw_settings.fw_update_configuration }}"
+
+# Update server BIOS from local file
+- name: Update server BIOS from local file
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "file"
+ update_type: "bios"
+ file_name: "{{ bios_filename }}"
+ delegate_to: localhost
+
+# Update iRMC FW
+- name: Update iRMC FW via TFTP
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "tftp"
+ update_type: "irmc"
+ server_name: "{{ tftp_server }}"
+ file_name: "{{ irmc_filename }}"
+ irmc_flash_selector: "Auto"
+ irmc_boot_selector: "Auto"
+ delegate_to: localhost
+```
+#### Return Values
+
+**For "update" command:**
+
+Default return values
+
+**fw_update_configuration returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| .BooterVersion | booter version | always | string | 8.08 |
+| .FirmwareBuildDate | firmware build date | always | string | Dec 1 2017 21:36:17 CEST |
+| .FirmwareRunningState | firmware running state | always | string | Inactive |
+| .FirmwareVersion | iRMC firmware version | always | string | 9.04F |
+| .ImageDescription | firmware image description | always | string | PRODUCTION RELEASE |
+| .SDRRId | sensor data record repository id | always | string | 308 |
+| .SDRRVersion | sensor data record repository version | always | string | 3.11 |
+| bios_file_name | BIOS file name | always | string | D3279-B1x.R1.20.0.UPC |
+| bios_version | current BIOS version | always | string | V5.0.0.11 R1.20.0 for D3279-B1x |
+| irmc_boot_selector | selector for iRMC FW to boot | always | string | MostRecentProgrammedFW |
+| irmc_file_name | iRMC Firmware image name | always | string | D3279_09.09F_sdr03.12.bin |
+| irmc_flash_selector | selector for iRMC FW to flash | always | string | Auto |
+| power_state | server power state | always | string | False |
+| tftp_server_name | TFTP server name | always | string | tftpserver.local |
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+
+---
+### irmc_getvm
#### Description
* Ansible module to get iRMC Virtual Media Data via iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
-| vm_type | No | CDImage | | the virtual media type whose data are to be read |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+| vm_type | No | CDImage | CDImage
FDImage
HDImage
| The virtual media type whose data are to be read. |
#### Examples
```yaml
@@ -246,9 +672,21 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| virtual_media_data | iRMC Virtual Media data | always | dict |
+**virtual_media_data returned by requesting data for e.g. 'CDImage':**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| CDImage | state of image | always | string | Connected |
+| bootmode | boot source override mode for the next boot | always | string | UEFI |
+| bootoverride | boot override type | always | string | Once |
+| bootsource | boot device override for next boot | always | string | BiosSetup |
+| image_name | name of the virtual image | always | string | mybootimage.iso |
+| server | remote server where the image is located | always | string | 192.168.2.1 |
+| share_name | path on the remote server where the image is located | always | string | isoimages |
+| share_type | share type (NFS or SMB) | always | string | NFS |
+| usb_attach_mode | remote image attach mode | always | string | AutoAttach |
+| user_domain | user domain for SMB share | always | string | local.net |
+| user_name | user name for SM share | always | string | test |
#### Notes
@@ -258,26 +696,26 @@
---
### irmc_idled
-
#### Description
* Ansible module to get or set server ID LED via iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| command | No | get | | get or set server ID LED state |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| state | No | | | desired server ID LED state for command 'set', ignored otherwise |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| command | No | get | get
set
| Get or set server ID LED state. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| state | No | | Off
Lit
Blinking
| Desired server ID LED state for command 'set', ignored otherwise. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -309,9 +747,15 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| idled_state | server ID LED state | always | str |
+**For command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| idled_state | server ID LED state | always | string | Blinking |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -321,49 +765,49 @@
---
### irmc_ldap
-
#### Description
* Ansible module to manage iRMC LDAP settings via iRMC remote scripting interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| alert_email_enabled | No | | | LDAP email alert enabled |
-| alert_table_refresh | No | | | LDAP alert table refresh in hours (0 = never) |
-| always_use_ssl | No | | | always use SSL login |
-| append_base_to_user_dn | No | | | append base DN to principal user DN |
-| auth_type | No | | - Automatic
- Settings stored on iRMC
- Settings stored on LDAP
| authorization type |
-| backup_port | No | | | non-SL port of backup LDAP server |
-| backup_server | No | | | backup LDAP server |
-| backup_ssl_port | No | | | SSL port of backup LDAP server |
-| base_dn | No | | | base DN |
-| command | No | get | | get or set iRMC LDAP data |
-| department_name | No | | | department name |
-| directory_type | No | | - MS Active Directory
- Novell eDirector
- Sun ePlanet
- OpenLDAP
- OpenDS / OpenDJ
| directory server type |
-| domain_name | No | | | domain name |
-| enabled | No | | | LDAP enabled |
-| enhanced_user_login | No | | | enhanced user login |
-| group_dn | No | | | groups directory as sub-tree from base DN |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| ldap_password | No | | | LDAP user password |
-| ldap_user | No | | | LDAP user name |
-| local_login_disabled | No | | | local login disabled |
-| primary_port | No | | | non-SL port of primary LDAP server |
-| primary_server | No | | | primary LDAP server |
-| primary_ssl_port | No | | | SSL port of primary LDAP server |
-| ssl_enabled | No | | | LDAP SSL enabled |
-| user_dn | No | | | principal user DN |
-| user_login_filter | No | | | user login search filter |
-| user_search_context | No | | | user search context |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| alert_email_enabled | No | | | LDAP email alert enabled. |
+| alert_table_refresh | No | | | LDAP alert table refresh in hours (0 = never). |
+| always_use_ssl | No | | | Always use SSL login. |
+| append_base_to_user_dn | No | | | Append base DN to principal user DN. |
+| auth_type | No | | Automatic
Stored on iRMC
Stored on LDAP
| Authorization type. |
+| backup_port | No | | | Non-SL port of backup LDAP server. |
+| backup_server | No | | | Backup LDAP server. |
+| backup_ssl_port | No | | | SSL port of backup LDAP server. |
+| base_dn | No | | | Base DN. |
+| command | No | get | get
set
| Get or set iRMC LDAP data. |
+| department_name | No | | | Department name. |
+| directory_type | No | | MS Active Directory
Novell eDirector
Sun ePlanet
OpenLDAP
OpenDS / OpenDJ
| Directory server type. |
+| domain_name | No | | | Domain name. |
+| enabled | No | | | LDAP enabled. |
+| enhanced_user_login | No | | | Enhanced user login. |
+| group_dn | No | | | Groups directory as sub-tree from base DN. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| ldap_password | No | | | LDAP user password. |
+| ldap_user | No | | | LDAP user name. |
+| local_login_disabled | No | | | Local login disabled. |
+| primary_port | No | | | Non-SL port of primary LDAP server. |
+| primary_server | No | | | Primary LDAP server. |
+| primary_ssl_port | No | | | SSL port of primary LDAP server. |
+| ssl_enabled | No | | | LDAP SSL enabled. |
+| user_dn | No | | | Principal user DN. |
+| user_login_filter | No | | | User login search filter. |
+| user_search_context | No | | | User search context. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -396,9 +840,38 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| ldap | LDAP information | always | dict |
+**ldap data returned by command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| alert_email_enabled | LDAP email alert enabled | always | bool | False |
+| alert_table_refresh | LDAP alert table refresh in hours | always | string | 0 |
+| always_use_ssl | always use SSL login | always | bool | False |
+| append_base_to_user_dn | append base DN to principal user DN | always | bool | False |
+| auth_type | authorization type | always | string | Automatic |
+| backup_port | non-SL port of backup LDAP server | always | string | 389 |
+| backup_server | backup LDAP server | always | string | ldap_backup.local |
+| backup_ssl_port | SSL port of backup LDAP server | always | string | 636 |
+| base_dn | base DN | always | string | |
+| department_name | department name | always | string | department |
+| directory_type | directory server type | always | string | MS Active Directory |
+| domain_name | domain name | always | string | domain.local |
+| enabled | LDAP enabled | always | bool | True |
+| enhanced_user_login | enhanced user login | always | string | False |
+| group_dn | groups directory as sub-tree from base DN | always | string | ou=ldaptest |
+| ldap_user | LDAP user name | always | string | Administrator |
+| local_login_disabled | local login disabled | always | bool | False |
+| primary_port | non-SL port of primary LDAP server | always | string | 389 |
+| primary_server | primary LDAP server | always | string | ldap_primary.local |
+| primary_ssl_port | SSL port of primary LDAP server | always | string | 636 |
+| ssl_enabled | LDAP SSL enabled | always | bool | False |
+| user_dn | principal user DN | always | string | |
+| user_login_filter | user login search filter | always | string | (&(objectclass=person)(cn=%s)) |
+| user_search_context | user search context | always | string | |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -408,25 +881,25 @@
---
### irmc_license
-
#### Description
* Ansible module to manage iRMC user accounts via iRMC remote scripting interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| command | No | get | | license key management to be executed |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| license_key | No | | | iRMC license key to be set |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| command | No | get | get
set
| License key management to be executed. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| license_key | No | | | iRMC license key to be set. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -458,9 +931,15 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| license_key | iRMC license key | always | string |
+**For command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| license_key | system-locked iRMC license key | always | string | |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -469,28 +948,104 @@
- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
---
-### irmc_powerstate
+### irmc_ntp
+
+#### Description
+* Ansible module to manage iRMC time options via iRMC remote scripting interface.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | get | get
set
| NTP management to be executed. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| ntp_server_primary | No | | | IP address (IPv4 or IPv6) or DNS name of primary NTP server. |
+| ntp_server_secondary | No | | | IP address (IPv4 or IPv6) or DNS name of secondary NTP server. |
+| rtc_mode | No | | local time
UTC/GMT
| Defines how iRMC interprets the system's hardware RTC time. |
+| time_mode | No | | System RTC
NTP
MMB NTP
| Defines how iRMC synchronizes its real-time clock (RTC). |
+| time_zone_location | No | | | iRMC time zone (e.g. 'Europe/Berlin'; based on Linux 'tzdata'). |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+
+#### Examples
+```yaml
+# Get iRMC time settings
+- name: Get iRMC time settingsa
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: time
+ delegate_to: localhost
+- name: Show iRMC time settings
+ debug:
+ msg: "{{ time.time_settings }}"
+
+# Set iRMC time option(s)
+- name: Set iRMC time option(s)
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ time_mode: "System RTC"
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**time_settings returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| ntp_server_primary | primary NTP server | always | string | pool.ntp.org |
+| ntp_server_secondary | secondary NTP server | always | string | pool.ntp.org |
+| rtc_mode | Defines how iRMC interprets the system's hardware RTC time | always | string | local time |
+| time_mode | Defines how iRMC synchronizes its real-time clock (RTC) | always | string | System RTC |
+| time_zone_location | iRMC time zone | always | string | Europe/Berlin |
+
+**For command "set":**
+
+Default return values
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/12563/wp-svs-irmc-remote-scripting-en.pdf
+- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
+
+---
+### irmc_powerstate
#### Description
* Ansible module to get or set server power state via iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| command | No | get | | get or set server power state |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| state | No | | - PowerOn
- PowerOff
- PowerCycle
- GracefulPowerOff
- ImmediateReset
- GracefulReset
- PulseNmi
- PressPowerButton
| desired server power state for command 'set', ignored otherwise; options 'GracefulPowerOff' and ' GracefulReset' require ServerView Agents running on server |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| command | No | get | get
set
| Get or set server power state. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| state | No | | PowerOn
PowerOff
PowerCycle
GracefulPowerOff
ImmediateReset
GracefulReset
PulseNmi
PressPowerButton
| Desired server power state for command 'set', ignored otherwise. Options 'GracefulPowerOff' and ' GracefulReset' require ServerView Agents running on server. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -522,9 +1077,15 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| power_state | server power state | always | str |
+**For command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| power_state | server power state | always | string | On |
+
+**For command "set":**
+
+Default return values
#### Notes
@@ -532,30 +1093,109 @@
- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
---
-### irmc_scci
+### irmc_profiles
+
+#### Description
+* Ansible module to configure the BIOS boot oder via iRMC.
+* Using this module may force server into several reboots.
+* See specification [iRMC RESTful API](http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf).
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * The PRIMERGY server needs to be at least a M2 model.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | list | list
get
create
delete
import
| How to handle iRMC profiles. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| profile | No | | | Which iRMC profile to handle. Only relevant for 'get', 'create', 'delete'. |
+| profile_json | No | | | Direct input of iRMC profile data. Only evaluated for command='import'. When set, 'profile_path' is ignored. |
+| profile_path | No | | | Path file where to read a profile. Only evaluated for command='import'. Ignored when 'profile_json' is set. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+| wait_for_finish | No | True | | Wait for 'create profile' or 'import profile' session to finish. Ignored otherwise. |
+
+#### Examples
+```yaml
+# List iRMC profiles
+- name: List iRMC profiles
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get iRMC HWConfigurationIrmc profile
+- name: Get iRMC HWConfigurationIrmc profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "HWConfigurationIrmc"
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**profiles returned for command "list":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| | name of specific profile | always | dict | BiosBootOrder |
+| .Location | RedFish location of profile | always | string | rest/v1/Oem/eLCM/ProfileManagement/BiosBootOrder |
+| .Name | name of profile | always | string | BiosBootOrder |
+**For all other commands:**
+
+Default return values
+
+**profile data returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| profile | data of requested profile | always | dict | |
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+
+---
+### irmc_scci
#### Description
* Ansible module to execute iRMC Remote Scripting (SCCI) commands.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| cabid | No | -1 (main cabinet) | | SCCI cabinet ID |
-| command | Yes | | - get_cs
- set_cs
- power_on
- power_off
- power_cycle
- reset
- nmi
- graceful_shutdown
- graceful_reboot
- cancel_shutdown
- reset_firmware
- connect_fd
- connect_cd
- connect_hd
| SCCI remote scripting command (opcode) |
-| data | No | | | data for commands which require data, ignored otherwise |
-| index | No | | | SCCI index |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| opcodeext | No | | | SCCI opcode extension |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| cabid | No | -1 (main cabinet) | | SCCI cabinet ID. |
+| command | Yes | | get_cs (ConfigSpace Read)
set_cs (ConfigSpace Write)
power_on (Power-On the Server)
power_off (Power-Off the Server)
power_cycle (Power Cycle the Server)
reset (Hard Reset the Server)
nmi (Pulse the NMI (Non Maskable Interrupt))
graceful_shutdown (Graceful Shutdown, requires running Agent)
graceful_reboot (Graceful Reboot, requires running Agent)
cancel_shutdown (Cancel a Shutdown Request)
reset_firmware (Perform a BMC Reset)
connect_fd (Connect/Disconnect a Floppy image on a Remote Share (iRMC S4 only))
connect_cd (Connect/Disconnect a CD/DVD image on a Remote Share (iRMC S4 only))
connect_hd (Connect/Disconnect a HDD image on a Remote Share (iRMC S4 only))
| SCCI remote scripting command. |
+| data | No | | | Data for commands which require data, ignored otherwise. |
+| index | No | | | SCCI index. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| opcodeext | No | | | SCCI opcode extension. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -588,9 +1228,15 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| data | SCCI command result | always | str |
+**For command "get_cs":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| data | result of requested SCCI command | always | string | In a galaxy far, far away ... |
+
+**For all other commands:**
+
+Default return values
#### Notes
@@ -598,29 +1244,109 @@
- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
---
-### irmc_setnextboot
+### irmc_session
+
+#### Description
+* Ansible module to handle iRMC sessions via Restful API.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | list | list
get
remove
terminate
clearall
| Handle iRMC sessions. |
+| id | No | | | Specific session to get, remove or terminate. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+
+#### Examples
+```yaml
+# List iRMC sessions
+- name: List iRMC sessions
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get specific session information
+- name: Get specific session information
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
+```
+#### Return Values
+
+**data for sessions returned for command "list":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| Duration | Session duration in seconds | always | int | 226 |
+| Id | session ID | always | string | 4 |
+| Start | work sequence | always | string | 2018/07/31 12:09:25 |
+| Status | session status | always | string | terminated regularly |
+| Tag | session tag | always | string | |
+| Text | work sequence | always | string | offlineUpdatePrepare |
+
+**session_log.SessionLog data returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| session_log.SessionLog.Entries.Entry | list of individual session log entries | always | list | |
+| session_log.SessionLog.Id | Session ID | always | int | 4 |
+| session_log.SessionLog.Tag | session tag | always | string | |
+| session_log.SessionLog.WorkSequence | work sequence | always | string | prepareOfflineUpdate |
+| session_status | session status | always | string | terminated regularly |
+
+**For all other commands:**
+
+Default return values
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+
+---
+### irmc_setnextboot
#### Description
* Ansible module to configure iRMC to force next boot to specified option.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| bootmode | No | | | the mode for the next boot |
-| bootoverride | No | Once | | boot override type |
-| bootsource | No | BiosSetup | - None
- Pxe
- Floppy
- Cd
- Hdd
- BiosSetup
| the source for the next boot |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| bootmode | No | | Legacy
UEFI
| The mode for the next boot. |
+| bootoverride | No | Once | Once
Continuous
| Boot override type. |
+| bootsource | No | BiosSetup | None
Pxe
Floppy
Cd
Hdd
BiosSetup
| The source for the next boot. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -640,9 +1366,7 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| result | nextboot action result | always | dict |
+Default return values
#### Notes
@@ -652,34 +1376,34 @@
---
### irmc_setvm
-
#### Description
* Ansible module to set iRMC Virtual Media Data via iRMC RedFish interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
* iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| force_mediatype_active | No | | | forces iRMC to activate one of the required remote media types |
-| force_remotemount_enabled | No | | | forces iRMC to enable the remote mount feature |
-| image | Yes | | | name of the remote image |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| server | Yes | | | remote server (IP or DNS name) where the image is located |
-| share | Yes | | | path on the remote server where the image is located |
-| share_type | No | | | share type (NFS share or SMB share) |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
-| vm_domain | No | | | user domain in case of SMB share |
-| vm_password | No | | | user password in case of SMB share |
-| vm_type | No | CDImage | | the virtual media type to be set |
-| vm_user | No | | | user account in case of SMB share |
+| force_mediatype_active | No | | | Forces iRMC to activate one of the required remote media types. |
+| force_remotemount_enabled | No | | | Forces iRMC to enable the remote mount feature. |
+| image | Yes | | | Name of the remote image. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| server | Yes | | | Remote server (IP or DNS name) where the image is located. |
+| share | Yes | | | Path on the remote server where the image is located. |
+| share_type | No | | NFS
SMB
| Share type (NFS share or SMB share). |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+| vm_domain | No | | | User domain in case of SMB share. |
+| vm_password | No | | | User password in case of SMB share. |
+| vm_type | No | CDImage | CDImage
FDImage
HDImage
| The virtual media type to be set. |
+| vm_user | No | | | User account in case of SMB share. |
#### Examples
```yaml
@@ -699,9 +1423,7 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| result | virtual media set action result | always | dict |
+Default return values
#### Notes
@@ -709,63 +1431,135 @@
- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
---
-### irmc_user
+### irmc_task
+
+#### Description
+* Ansible module to handle iRMC tasks via Restful API.
+* Module Version V1.1.
+
+#### Requirements
+ * The module needs to run locally.
+ * iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ * Python >= 2.6
+ * Python module 'future'
+
+#### Options
+
+| Parameter | Required | Default | Choices | Description |
+|:----------|:---------|:--------|:--------|:----------- |
+| command | No | list | list
get
| Handle iRMC tasks. |
+| id | No | | | Specific task to get. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
+#### Examples
+```yaml
+# List iRMC tasks
+- name: List iRMC tasks
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get specific task information
+- name: Get specific task information
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
+```
+
+#### Return Values
+
+**task data returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| EndTime | end time | always | string | 2018-07-31 12:26:44 |
+| Id | task ID | always | int | 3 |
+| Name | task name | always | string | ProfileParametersApply |
+| StartTime | start time | always | string | 2018-07-31 12:23:02 |
+| State | task state | always | string | Completed |
+| StateOem | Oem task state | always | string | LcmSessFinished |
+| StateProgressPercent | state progress in % | always | string | 100 |
+| TotalProgressPercent | overall progress in % | always | string | 100 |
+
+**tasks data returned for command "list":**
+
+List of individual task entries (see above)
+
+#### Notes
+
+- See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+- See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+
+---
+### irmc_user
#### Description
* Ansible module to manage iRMC user accounts via iRMC remote scripting interface.
-* Module Version V1.0.1.
+* Module Version V1.1.
#### Requirements
* The module needs to run locally.
- * python >= 2.6
+ * Python >= 2.6
+ * Python module 'future'
#### Options
| Parameter | Required | Default | Choices | Description |
|:----------|:---------|:--------|:--------|:----------- |
-| alert_fans | No | | | define alert level for fan sensors |
-| alert_hderrors | No | | | define alert level for disk drivers & controllers |
-| alert_hwerrors | No | | | define alert level for critical hardware errors |
-| alert_memory | No | | | define alert level for memory |
-| alert_network | No | | | define alert level for network interface |
-| alert_others | No | | | define alert level for other |
-| alert_posterrors | No | | | define alert level for POST errors |
-| alert_power | No | | | define alert level for system power |
-| alert_remote | No | | | define alert level for remote management |
-| alert_security | No | | | define alert level for security |
-| alert_syshang | No | | | define alert level for system hang |
-| alert_sysstatus | No | | | define alert level for system status |
-| alert_temperatures | No | | | define alert level for temperature sensors |
-| avr_enabled | No | | | user may use Advanved Video Redirection (AVR) |
-| command | No | get | | user management to be executed |
-| config_bmc_enabled | No | | | user may configure iRMC settings |
-| config_user_enabled | No | | | user may configure user accounts |
-| description | No | | | user account desciption |
-| email_address | No | | | alert email address |
-| email_enabled | No | | | alert email enabled |
-| email_encrypted | No | | | alert email is encrypted |
-| email_server | No | | - Automatic
- Primary
- Secondary
| preferred mail server for alert email |
-| email_type | No | | - Standard
- ITS-Format
- REMCS
- Fixed Subject
- SMS
| alert email format |
-| enabled | No | | | user account enabled |
-| irmc_password | Yes | | | password for iRMC user for basic authentication |
-| irmc_url | Yes | | | IP address of the iRMC to be requested for data |
-| irmc_username | Yes | | | iRMC user for basic authentication |
-| lan_privilege | No | | - Reserved
- Callback
- User
- Operator
- Administrator
- OEM
- NoAccess
| IPMI LAN channel privilege |
-| name | Yes | | | user account name |
-| password | No | | | user account password |
-| redfish_enabled | No | | | user may use iRMC Redfish interface |
-| redfish_role | No | | - NoAccess
- Operator
- Administrator
- ReadOnly
| user account Redfish role |
-| serial_privilege | No | | - Reserved
- Callback
- User
- Operator
- Administrator
- OEM
- NoAccess
| IPMI serial channel privilege |
-| shell | No | | - SMASH CLP
- CLI
- Remote Manager
- IPMI basic mode
- IPMI terminal mode
- None
| user text access type |
-| snmpv3_access | No | | | user account SNMPV3 access privilege |
-| snmpv3_auth | No | | | user account SNMPv3 authentication |
-| snmpv3_enabled | No | | | user may use SNMPv3 |
-| snmpv3_privacy | No | | | user account SNMPv3 privacy type |
-| ssh_certificate | No | | | user account SSH certificate |
-| ssh_public_key | No | | | user account SSH public key |
-| storage_enabled | No | | | user may use Remote Storage |
-| validate_certs | No | True | | evaluate SSL certificate (set to false for self-signed certificate) |
+| alert_fans | No | | None
Critical
Warning
All
| Define alert level for fan sensors. |
+| alert_hderrors | No | | None
Critical
Warning
All
| Define alert level for disk drivers & controllers. |
+| alert_hwerrors | No | | None
Critical
Warning
All
| Define alert level for critical hardware errors. |
+| alert_memory | No | | None
Critical
Warning
All
| Define alert level for memory. |
+| alert_network | No | | None
Critical
Warning
All
| Define alert level for network interface. |
+| alert_others | No | | None
Critical
Warning
All
| Define alert level for other. |
+| alert_posterrors | No | | None
Critical
Warning
All
| Define alert level for POST errors. |
+| alert_power | No | | None
Critical
Warning
All
| Define alert level for system power. |
+| alert_remote | No | | None
Critical
Warning
All
| Define alert level for remote management. |
+| alert_security | No | | None
Critical
Warning
All
| Define alert level for security. |
+| alert_syshang | No | | None
Critical
Warning
All
| Define alert level for system hang. |
+| alert_sysstatus | No | | None
Critical
Warning
All
| Define alert level for system status. |
+| alert_temperatures | No | | None
Critical
Warning
All
| Define alert level for temperature sensors. |
+| avr_enabled | No | | | User may use Advanved Video Redirection (AVR) |
+| command | No | get | get
create
change
delete
| User management to be executed. |
+| config_bmc_enabled | No | | | User may configure iRMC settings. |
+| config_user_enabled | No | | | User may configure user accounts. |
+| description | No | | | User account desciption. |
+| email_address | No | | | Alert email address. |
+| email_enabled | No | | | Alert email enabled. |
+| email_encrypted | No | | | Alert email is encrypted. |
+| email_server | No | | Automatic
Primary
Secondary
| Preferred mail server for alert email. |
+| email_type | No | | Standard
ITS-Format
REMCS
Fixed Subject
SMS
| Alert email format. |
+| enabled | No | | | User account enabled. |
+| irmc_password | Yes | | | Password for iRMC user for basic authentication. |
+| irmc_url | Yes | | | IP address of the iRMC to be requested for data. |
+| irmc_username | Yes | | | iRMC user for basic authentication. |
+| lan_privilege | No | | Reserved
Callback
User
Operator
Administrator
OEM
NoAccess
| IPMI LAN channel privilege. |
+| name | Yes | | | User account name. |
+| password | No | | | User account password. |
+| redfish_enabled | No | | | User may use iRMC Redfish interface. |
+| redfish_role | No | | NoAccess
Operator
Administrator
ReadOnly
| User account Redfish role. |
+| serial_privilege | No | | Reserved
Callback
User
Operator
Administrator
OEM
NoAccess
| IPMI serial channel privilege. |
+| shell | No | | SMASH CLP
CLI
Remote Manager
IPMI basic mode
IPMI terminal mode
None
| User text access type. |
+| snmpv3_access | No | | ReadOnly
ReadWrite
Other
| User account SNMPV3 access privilege. |
+| snmpv3_auth | No | | Undefined
SHA
MD5
None
| User account SNMPv3 authentication. |
+| snmpv3_enabled | No | | | User may use SNMPv3. |
+| snmpv3_privacy | No | | Undefined
AES
DES
None
| User account SNMPv3 privacy type. |
+| ssh_certificate | No | | | User account SSH certificate. |
+| ssh_public_key | No | | | user account SSH public key. |
+| storage_enabled | No | | | User may use Remote Storage. |
+| validate_certs | No | True | | Evaluate SSL certificate (set to false for self-signed certificate). |
#### Examples
```yaml
@@ -822,9 +1616,51 @@
#### Return Values
-| Name | Description | Returned | Type |
-|:-----|:------------|:---------|:-----|
-| user | user account information | always | dict |
+**user data returned for command "get":**
+
+| Name | Description | Returned | Type | Example |
+|:-----|:------------|:---------|:-----|:--------|
+| alert_fans | alert level for fan sensors | always | string | Warning |
+| alert_hderrors | alert level for disk drivers & controllers | always | string | Critical |
+| alert_hwerrors | alert level for critical hardware errors | always | string | All |
+| alert_memory | alert level for memory | always | string | Critical |
+| alert_network | alert level for network interface | always | string | Warning |
+| alert_others | alert level for other | always | string | None |
+| alert_posterrors | alert level for POST errors | always | string | All |
+| alert_power | alert level for system power | always | string | Warning |
+| alert_remote | alert level for remote management | always | string | Critical |
+| alert_security | alert level for security | always | string | Warning |
+| alert_syshang | alert level for system hang | always | string | Critical |
+| alert_sysstatus | alert level for system status | always | string | None |
+| alert_temperatures | alert level for temperature sensors | always | string | Warning |
+| avr_enabled | user may use Advanved Video Redirection (AVR) | always | bool | True |
+| config_bmc_enabled | user may configure iRMC settings | always | bool | True |
+| config_user_enabled | user may configure user accounts | always | bool | True |
+| description | user account desciption | always | string | Admin User |
+| email_address | alert email address | always | string | admin@irmc.local |
+| email_enabled | alert email enabled | always | bool | False |
+| email_encrypted | alert email is encrypted | always | bool | False |
+| email_server | preferred mail server for alert email | always | string | Automatic |
+| email_type | alert email format | always | string | Standard |
+| enabled | user account enabled | always | bool | True |
+| id | user ID | always | int | 0 |
+| lan_privilege | IPMI LAN channel privilege | always | string | Administrator |
+| name | user account name | always | string | admin |
+| redfish_enabled | user may use iRMC Redfish interface | always | bool | True |
+| redfish_role | user account Redfish role | always | string | Administrator |
+| serial_privilege | IPMI serial channel privilege | always | string | Administrator |
+| shell | user text access type | always | string | Remote Manager |
+| snmpv3_access | user account SNMPV3 access privilege | always | string | ReadOnly |
+| snmpv3_auth | user account SNMPv3 authentication | always | string | SHA |
+| snmpv3_enabled | user may use SNMPv3 | always | bool | False |
+| snmpv3_privacy | user account SNMPv3 privacy type | always | string | DES |
+| ssh_certificate | user account SSH certificate | always | string | |
+| ssh_public_key | user account SSH public key | always | string | |
+| storage_enabled | user may use Remote Storage | always | bool | True |
+
+**For all other commands:**
+
+Default return values
#### Notes
@@ -832,5 +1668,10 @@
- See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
---
+---
+FUJITSU LIMITED
+Copyright 2018 FUJITSU LIMITED
+
+GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
diff --git a/LICENSE.md b/LICENSE.md
index 1488ea9..c154efd 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -674,5 +674,3 @@ Public License instead of this License. But first, please read
.
-This work is licensed under the Creative Commons Attribution 3.0 Unported License.
-To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/deed.en_US.
diff --git a/README.md b/README.md
index 5ce84c3..e6c471f 100644
--- a/README.md
+++ b/README.md
@@ -2,14 +2,14 @@
# Manage Fujitsu PRIMERGY servers via iRMC
The Fujitsu Software Serverview Ansible iRMC Integration features modules to access and manage
-PRIMERGY servers via iRMC.
+Fujitsu PRIMERGY servers via iRMC.
#### Table of Contents
1. [Overview](#overview)
2. [Requirements](#requirements)
3. [Getting Started](#getting-started)
-4. [API Documentation](#api-documentation)
+4. [API Documentation](#documentation)
5. [Usage](#usage)
6. [Modules](#modules)
7. [Playbooks](#playbooks)
@@ -22,13 +22,14 @@ PRIMERGY servers via iRMC.
These modules and examples are intended to provide easy-to-follow and understandable solutions to manage
Fujitsu PRIMERY server settings via iRMC.
-##### Version: 1.0.1
+##### Version: V1.1
## Requirements
+- Fujitsu PRIMERGY Server with iRMC S4 FW >= 9.04 or iRMC S5 FW >= 1.25
- Ansible >= 2.1
- Python >= 2.6
-- PRIMERGY Server with iRMC S4 FW >= 9.04 or iRMC S5 FW >= 1.25
+- Python module 'future'
## Getting started
@@ -64,17 +65,25 @@ Run the verbose version with:
The following modules are part of this project:
+ * irmc_biosbootorder - configure iRMC to force next boot to specified option
* irmc_certificate - manage iRMC certificates
+ * irmc_compare_profiles - compare two iRMC profiles
* irmc_connectvm - connect iRMC Virtual Media Data
- * irmc_facts - get or set basic iRMC and PRIMERGY server data
+ * irmc_eventlog - handle iRMC eventlogs
+ * irmc_facts - get or set Fujitsu PRIMERGY server and iRMC facts
+ * irmc_fwbios_update - update iRMC Firmware or server BIOS
* irmc_getvm - get iRMC Virtual Media Data
* irmc_idled - get or set server ID LED
* irmc_ldap - manage iRMC LDAP settings
* irmc_license - manage iRMC user accounts
+ * irmc_ntp - manage iRMC time options
* irmc_powerstate - get or set server power state
+ * irmc_profiles - handle iRMC profiles
* irmc_scci - execute iRMC remote SCCI commands
+ * irmc_session - handle iRMC sessions
* irmc_setnextboot - configure iRMC to force next boot to specified option
* irmc_setvm - set iRMC Virtual Media Data
+ * irmc_task - handle iRMC tasks
* irmc_user - manage iRMC user accounts
For details please refer to the [Module Documentation](DOCUMENTATION.md)
@@ -83,41 +92,54 @@ For details please refer to the [Module Documentation](DOCUMENTATION.md)
The following playbooks are part of this package to demonstrate the usage of the modules:
+ * irmc_biosbootorder_examples.yml
+ * irmc_cas_examples.yml
* irmc_certificate_examples.yml
+ * irmc_compare_profiles_examples.yml
* irmc_connectvm_examples.yml
+ * irmc_eventlog_examples.yml
* irmc_facts_examples.yml
+ * irmc_fwbios_update_examples.yml
* irmc_getvm_examples.yml
* irmc_idled_examples.yml
* irmc_ldap_examples.yml
* irmc_license_examples.yml
+ * irmc_ntp_examples.yml
* irmc_powerstate_examples.yml
+ * irmc_profiles_examples.yml
* irmc_scci_examples.yml
+ * irmc_session_examples.yml
* irmc_setnextboot_examples.yml
* irmc_setvm_examples.yml
+ * irmc_task_examples.yml
* irmc_user_examples.yml
The following playbooks are part of this package to demonstrate the solution for common
bare-metal-server provisioning tasks:
-* boot_to_virtual_cd.yml
-* create_new_user_and_remove_old_user.yml
-* create_user_from_file.yml
-* export_ldap_settings_to_file.yml
-* export_user_data_to_file.yml
-* get_server_facts.yml
-* import_ldap_settings_from_file.yml
+ * boot_to_virtual_cd.yml
+ * compare_server_configuration_against_saved_profiles.yml
+ * create_new_user_and_remove_old_user.yml
+ * create_user_from_file.yml
+ * export_ldap_settings_to_file.yml
+ * export_server_configuration_profiles_to_files.yml
+ * export_user_data_to_file.yml
+ * get_server_facts.yml
+ * import_ldap_settings_from_file.yml
+ * set_bios_boot_order.yml
+ * set_bios_boot_order_default.yml
## Change log
-* V1.0.0: Initial version
-* V1.0.1: Minor changes and bug fixes from QA run
+* V1.0: Initial version
+* V1.1: New: iRMC FW/BIOS update, BIOS boot order, iRMC profile management
## License
-FUJITSU Limited
+FUJITSU LIMITED
Copyright 2018 FUJITSU LIMITED
-GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
## Authors
diff --git a/examples/boot_to_virtual_cd.yml b/examples/boot_to_virtual_cd.yml
index 3776b08..2ed86ca 100644
--- a/examples/boot_to_virtual_cd.yml
+++ b/examples/boot_to_virtual_cd.yml
@@ -31,7 +31,7 @@
# vm_user: "domain_user"
# vm_password: "domain_password"
- gather_facts: no
+ gather_facts: false
tasks:
- name: Get system power state
@@ -117,4 +117,3 @@
command: "set"
state: "PowerOn"
delegate_to: localhost
-
diff --git a/examples/compare_server_configuration_against_saved_profiles.yml b/examples/compare_server_configuration_against_saved_profiles.yml
new file mode 100644
index 0000000..8aa21bf
--- /dev/null
+++ b/examples/compare_server_configuration_against_saved_profiles.yml
@@ -0,0 +1,80 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook to compare iRMC system and hardware configuration against
+# existing profiles files in JSON format
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: compare iRMC system and hardware configuration against existing profiles
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+ # path to files with saved iRMC profiles
+ # irmc_sysconfig_file: "Profile_SystemConfigSave.json"
+ # irmc_hwconfig_file: "Profile_HWConfigurationSave.json"
+
+ gather_facts: false
+
+ tasks:
+ # Get iRMC profiles
+ - name: Get SystemConfig profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "SystemConfig"
+ register: system_config
+ delegate_to: localhost
+ - name: Get HWConfigurationIrmc profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "HWConfigurationIrmc"
+ register: hardware_config
+ delegate_to: localhost
+ - name: Show SystemConfig profile
+ debug:
+ msg: "{{ system_config.profile }}"
+ - name: Show HWConfigurationIrmc profile
+ debug:
+ msg: "{{ hardware_config.profile }}"
+
+ # compare iRMC Profiles to file
+ - name: Compare SystemConfig profile
+ irmc_compare:
+ profile_path1: "{{ irmc_sysconfig_file }}"
+ profile_json2: "{{ system_config.profile }}"
+ register: system_config_result
+ delegate_to: localhost
+ - fail:
+ msg:
+ - SystemConfig profile differs from saved profile
+ - "{{ system_config_result.comparison_list }}"
+ when: system_config_result.comparison_result == false
+ - name: Compare HWConfigurationIrmc profile
+ irmc_compare:
+ profile_path1: "{{ irmc_hwconfig_file }}"
+ profile_json2: "{{ hardware_config.profile }}"
+ register: hardware_config_result
+ delegate_to: localhost
+ - fail:
+ msg:
+ - HWConfigurationIrmc profile differs from saved profile
+ - "{{ hardware_config_result.comparison_list }}"
+ when: hardware_config_result.comparison_result == false
+
diff --git a/examples/create_new_user_and_remove_old_user.yml b/examples/create_new_user_and_remove_old_user.yml
index 7d81421..2a73640 100644
--- a/examples/create_new_user_and_remove_old_user.yml
+++ b/examples/create_new_user_and_remove_old_user.yml
@@ -25,7 +25,7 @@
# Note: make sure this user does not yet exist
# new_user: "root"
# new_password: "root"
- gather_facts: no
+ gather_facts: false
tasks:
# Get original user account data
@@ -115,4 +115,3 @@
command: "delete"
name: "{{ original_user }}"
delegate_to: localhost
-
diff --git a/examples/create_user_from_file.yml b/examples/create_user_from_file.yml
index 81897c6..3a40dce 100644
--- a/examples/create_user_from_file.yml
+++ b/examples/create_user_from_file.yml
@@ -3,7 +3,7 @@
# Copyright 2018 FUJITSU LIMITED
# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
-# example playbook get user data from iRMC and store
+# example playbook to get user data from iRMC and store
# in a file in JSON format
# variables not defined in this playbook are expected to be provided
@@ -25,7 +25,7 @@
# user_data_file: "UserData.json"
user_data: "{{ lookup('file','{{ user_data_file }}') | from_json }}"
- gather_facts: no
+ gather_facts: false
tasks:
- name: Create new user account
@@ -73,4 +73,3 @@
alert_memory: "{{ user_data.alert_memory }}"
alert_others: "{{ user_data.alert_others }}"
delegate_to: localhost
-
diff --git a/examples/export_ldap_settings_to_file.yml b/examples/export_ldap_settings_to_file.yml
index 861c4df..19322eb 100644
--- a/examples/export_ldap_settings_to_file.yml
+++ b/examples/export_ldap_settings_to_file.yml
@@ -23,7 +23,7 @@
# make sure to not overwrite this file if you do not intend to
# ldap_settings_file: "LdapSettings.json"
- gather_facts: no
+ gather_facts: false
tasks:
# Get LDAP data
diff --git a/examples/export_server_configuration_profiles_to_files.yml b/examples/export_server_configuration_profiles_to_files.yml
new file mode 100644
index 0000000..4d614af
--- /dev/null
+++ b/examples/export_server_configuration_profiles_to_files.yml
@@ -0,0 +1,66 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook to export iRMC system and hardware configuration and store
+# it in a file in JSON format
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: get iRMC system and hardware configuration and store in file
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+ # path to files to save iRMC profiles to
+ # make sure to not overwrite these files if you do not intend to
+ # irmc_sysconfig_file: "Profile_SystemConfigSave.json"
+ # irmc_hwconfig_file: "Profile_HWConfigurationSave.json"
+
+ gather_facts: false
+
+ tasks:
+ # Get iRMC profiles
+ - name: Get SystemConfig profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "SystemConfig"
+ register: system_config
+ delegate_to: localhost
+ - name: Get HWConfigurationIrmc profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "HWConfigurationIrmc"
+ register: hardware_config
+ delegate_to: localhost
+ - name: Show SystemConfig profile
+ debug:
+ msg: "{{ system_config.profile }}"
+ - name: Show HWConfigurationIrmc profile
+ debug:
+ msg: "{{ hardware_config.profile }}"
+
+ # Write iRMC Profiles to file
+ - name: Write SystemConfig profile
+ copy:
+ content: "{{ system_config.ldap }}"
+ dest: "{{ irmc_sysconfig_file }}"
+ - name: Write HWConfigurationIrmc profile
+ copy:
+ content: "{{ hardware_config.ldap }}"
+ dest: "{{ irmc_hwconfig_file }}"
diff --git a/examples/export_user_data_to_file.yml b/examples/export_user_data_to_file.yml
index 21b316a..dc8e516 100644
--- a/examples/export_user_data_to_file.yml
+++ b/examples/export_user_data_to_file.yml
@@ -24,7 +24,7 @@
# user_data_file: "UserData.json"
# export_user: "admin"
- gather_facts: no
+ gather_facts: false
tasks:
# Get user account data
diff --git a/examples/get_server_facts.yml b/examples/get_server_facts.yml
index 30d2592..2352715 100644
--- a/examples/get_server_facts.yml
+++ b/examples/get_server_facts.yml
@@ -19,7 +19,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get basic server and iRMC facts
@@ -49,4 +49,3 @@
- name: Show Virtual Media data
debug:
msg: "{{ vmdata.virtual_media_data }}"
-
diff --git a/examples/import_ldap_settings_from_file.yml b/examples/import_ldap_settings_from_file.yml
index 6f96109..5020cce 100644
--- a/examples/import_ldap_settings_from_file.yml
+++ b/examples/import_ldap_settings_from_file.yml
@@ -25,7 +25,7 @@
# ldap_password: "ldap_password"
ldap_settings: "{{ lookup('file','{{ ldap_settings_file }}') | from_json }}"
- gather_facts: no
+ gather_facts: false
tasks:
- name: Import LDAP Settings
@@ -61,4 +61,3 @@
alert_email_enabled: "{{ ldap_settings.alert_email_enabled }}"
alert_table_refresh: "{{ ldap_settings.alert_table_refresh }}"
delegate_to: localhost
-
diff --git a/examples/irmc_biosbootorder_examples.yml b/examples/irmc_biosbootorder_examples.yml
new file mode 100644
index 0000000..558af51
--- /dev/null
+++ b/examples/irmc_biosbootorder_examples.yml
@@ -0,0 +1,46 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_biosbootorder'
+# to configure iRMC to force next boot to specified option
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_biosbootorder - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Get Bios Boot Order
+ - name: Get Bios Boot Order
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ force_new: false
+ delegate_to: localhost
+
+ # Set Bios Boot Order to default
+ - name: Get Bios Boot Order to default
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "default"
+ ignore_power_on: false
+ delegate_to: localhost
diff --git a/examples/irmc_cas_examples.yml b/examples/irmc_cas_examples.yml
new file mode 100644
index 0000000..905acf9
--- /dev/null
+++ b/examples/irmc_cas_examples.yml
@@ -0,0 +1,50 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_cas'
+# to manage iRMC CAS settings
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_cas - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Get CAS data
+ - name: Get CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: cas
+ delegate_to: localhost
+ - name: Show iRMC CAS data
+ debug:
+ msg: "{{ cas.cas }}"
+
+ # Set CAS data
+ - name: Set CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ cas_user: "username"
+ cas_password: "password"
+ delegate_to: localhost
diff --git a/examples/irmc_certificate_examples.yml b/examples/irmc_certificate_examples.yml
index 30f0df7..235f3d3 100644
--- a/examples/irmc_certificate_examples.yml
+++ b/examples/irmc_certificate_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get SSL certificates
@@ -49,4 +49,3 @@
ssl_cert_path: "{{ ssl_cert_path }}"
ssl_ca_cert_path: "{{ ssl_ca_cert_path }}"
delegate_to: localhost
-
diff --git a/examples/irmc_compare_profiles_examples.yml b/examples/irmc_compare_profiles_examples.yml
new file mode 100644
index 0000000..9f2246b
--- /dev/null
+++ b/examples/irmc_compare_profiles_examples.yml
@@ -0,0 +1,31 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_compare_profiles'
+# to compare two iRMC profiles
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_compare_profiles - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Compare iRMC profiles against each other
+ - name: Compare iRMC profiles
+ irmc_compare_profiles:
+ profile_path1: "{{ profile1_path }}"
+ profile_path2: "{{ profile2_path }}"
+ delegate_to: localhost
diff --git a/examples/irmc_connectvm_examples.yml b/examples/irmc_connectvm_examples.yml
index 88cfc65..916505e 100644
--- a/examples/irmc_connectvm_examples.yml
+++ b/examples/irmc_connectvm_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Disconnect Virtual CD
@@ -42,4 +42,3 @@
validate_certs: "{{ validate_certificate }}"
command: "ConnectCD"
delegate_to: localhost
-
diff --git a/examples/irmc_eventlog_examples.yml b/examples/irmc_eventlog_examples.yml
new file mode 100644
index 0000000..3c807cf
--- /dev/null
+++ b/examples/irmc_eventlog_examples.yml
@@ -0,0 +1,46 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_eventlog'
+# to handle iRMC eventlogs
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_eventlog - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # List iRMC InternalEventLog
+ - name: List iRMC InternalEventLog
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ eventlog_type: "InternalEventLog"
+ delegate_to: localhost
+
+ # Get specific SystemEventLog entry information
+ - name: Get specific SystemEventLog entry information
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 0
+ delegate_to: localhost
diff --git a/examples/irmc_facts_examples.yml b/examples/irmc_facts_examples.yml
index d006a86..13c5775 100644
--- a/examples/irmc_facts_examples.yml
+++ b/examples/irmc_facts_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get basic server and iRMC facts
@@ -47,4 +47,3 @@
command: "set"
asset_tag: "Ansible test server"
delegate_to: localhost
-
diff --git a/examples/irmc_fwbios_update_examples.yml b/examples/irmc_fwbios_update_examples.yml
new file mode 100644
index 0000000..28eaa2d
--- /dev/null
+++ b/examples/irmc_fwbios_update_examples.yml
@@ -0,0 +1,67 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_fwbios_update'
+# to update iRMC Firmware or server BIOS
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_fwbios_update - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Get irmc firmware and BIOS update settings
+ - name: Get irmc firmware and BIOS update settings
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: fw_settings
+ delegate_to: localhost
+ - name: Show irmc firmware and BIOS update settings
+ debug:
+ msg: "{{ fw_settings.fw_update_configuration }}"
+
+ # Update server BIOS from local file
+ - name: Update server BIOS from local file
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "file"
+ update_type: "bios"
+ file_name: "{{ bios_filename }}"
+ delegate_to: localhost
+
+ # Update iRMC FW
+ - name: Update iRMC FW via TFTP
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "tftp"
+ update_type: "irmc"
+ server_name: "{{ tftp_server }}"
+ file_name: "{{ irmc_filename }}"
+ irmc_flash_selector: "Auto"
+ irmc_boot_selector: "Auto"
+ delegate_to: localhost
diff --git a/examples/irmc_getvm_examples.yml b/examples/irmc_getvm_examples.yml
index 9546f39..86ade44 100644
--- a/examples/irmc_getvm_examples.yml
+++ b/examples/irmc_getvm_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get Virtual Media data
@@ -36,4 +36,3 @@
- name: Show Virtual Media data
debug:
msg: "{{ vmdata.virtual_media_data }}"
-
diff --git a/examples/irmc_idled_examples.yml b/examples/irmc_idled_examples.yml
index 37d6b54..de33565 100644
--- a/examples/irmc_idled_examples.yml
+++ b/examples/irmc_idled_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get server ID LED state
@@ -47,4 +47,3 @@
command: "set"
state: "Lit"
delegate_to: localhost
-
diff --git a/examples/irmc_ldap_examples.yml b/examples/irmc_ldap_examples.yml
index 1cdfde0..50e4955 100644
--- a/examples/irmc_ldap_examples.yml
+++ b/examples/irmc_ldap_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get LDAP data
@@ -48,4 +48,3 @@
ldap_user: "username"
ldap_password: "password"
delegate_to: localhost
-
diff --git a/examples/irmc_license_examples.yml b/examples/irmc_license_examples.yml
index dde4416..6ed4f64 100644
--- a/examples/irmc_license_examples.yml
+++ b/examples/irmc_license_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get iRMC license key
@@ -47,4 +47,3 @@
command: "set"
license_key: "{{ license_key }}"
delegate_to: localhost
-
diff --git a/examples/irmc_ntp_examples.yml b/examples/irmc_ntp_examples.yml
new file mode 100644
index 0000000..14f1f9e
--- /dev/null
+++ b/examples/irmc_ntp_examples.yml
@@ -0,0 +1,49 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_ntp'
+# to manage iRMC time options
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_ntp - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Get iRMC time settings
+ - name: Get iRMC time settingsa
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: time
+ delegate_to: localhost
+ - name: Show iRMC time settings
+ debug:
+ msg: "{{ time.time_settings }}"
+
+ # Set iRMC time option(s)
+ - name: Set iRMC time option(s)
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ time_mode: "System RTC"
+ delegate_to: localhost
diff --git a/examples/irmc_powerstate_examples.yml b/examples/irmc_powerstate_examples.yml
index 310b40b..f497739 100644
--- a/examples/irmc_powerstate_examples.yml
+++ b/examples/irmc_powerstate_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Get server power state
@@ -47,4 +47,3 @@
command: "set"
state: "PowerOn"
delegate_to: localhost
-
diff --git a/examples/irmc_profiles_examples.yml b/examples/irmc_profiles_examples.yml
new file mode 100644
index 0000000..7cf8b14
--- /dev/null
+++ b/examples/irmc_profiles_examples.yml
@@ -0,0 +1,45 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_profiles'
+# to handle iRMC profiles
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_profiles - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # List iRMC profiles
+ - name: List iRMC profiles
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+ # Get iRMC HWConfigurationIrmc profile
+ - name: Get iRMC HWConfigurationIrmc profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "HWConfigurationIrmc"
+ delegate_to: localhost
diff --git a/examples/irmc_scci_examples.yml b/examples/irmc_scci_examples.yml
index 370812b..71a287d 100644
--- a/examples/irmc_scci_examples.yml
+++ b/examples/irmc_scci_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Write server location
@@ -48,4 +48,3 @@
- name: Show server location
debug:
msg: "{{ scci.data }}"
-
diff --git a/examples/irmc_session_examples.yml b/examples/irmc_session_examples.yml
new file mode 100644
index 0000000..0b90015
--- /dev/null
+++ b/examples/irmc_session_examples.yml
@@ -0,0 +1,45 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_session'
+# to handle iRMC sessions
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_session - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # List iRMC sessions
+ - name: List iRMC sessions
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+ # Get specific session information
+ - name: Get specific session information
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
diff --git a/examples/irmc_setnextboot_examples.yml b/examples/irmc_setnextboot_examples.yml
index 08bb8ce..93f9774 100644
--- a/examples/irmc_setnextboot_examples.yml
+++ b/examples/irmc_setnextboot_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Set Bios to next boot from Virtual CD
@@ -35,4 +35,3 @@
bootoverride: "Once"
bootmode: "Legacy"
delegate_to: localhost
-
diff --git a/examples/irmc_setvm_examples.yml b/examples/irmc_setvm_examples.yml
index e7b81c5..5769b30 100644
--- a/examples/irmc_setvm_examples.yml
+++ b/examples/irmc_setvm_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Set Virtual Media Data
@@ -35,4 +35,3 @@
image: "{{ image }}"
share_type: "{{ share_type }}"
delegate_to: localhost
-
diff --git a/examples/irmc_task_examples.yml b/examples/irmc_task_examples.yml
new file mode 100644
index 0000000..1ca9b5b
--- /dev/null
+++ b/examples/irmc_task_examples.yml
@@ -0,0 +1,45 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook for module 'irmc_task'
+# to handle iRMC tasks
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: irmc_task - usage examples
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # List iRMC tasks
+ - name: List iRMC tasks
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+ # Get specific task information
+ - name: Get specific task information
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
diff --git a/examples/irmc_user_examples.yml b/examples/irmc_user_examples.yml
index 6a01d97..2a8aa5c 100644
--- a/examples/irmc_user_examples.yml
+++ b/examples/irmc_user_examples.yml
@@ -20,7 +20,7 @@
# Note: set validate_certificate to false for self-signed certificate
# validate_certificate: false
- gather_facts: no
+ gather_facts: false
tasks:
# Create new user account
@@ -72,4 +72,3 @@
command: "delete"
name: "ansibleuser"
delegate_to: localhost
-
diff --git a/examples/set_bios_boot_order.yml b/examples/set_bios_boot_order.yml
new file mode 100644
index 0000000..3d25e39
--- /dev/null
+++ b/examples/set_bios_boot_order.yml
@@ -0,0 +1,46 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook to set BIOS boot order permanently and
+# specify 'next boot' device
+
+# Notes:
+# - this playbook will abort if the server is 'on'
+# - this playbook will cause the server to reboot a few times
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: set BIOS boot order permanently and 'next boot' device
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+ # boot_key: "DeviceName",
+ # boot_device: "(Bus 02 Dev 00)PCI RAID Adapter"
+ # next_boot_device: "HL-DT-ST DVDRAM GHB0N"
+
+ gather_facts: false
+
+ tasks:
+ # Set Bios Boot Order
+ - name: Set BIOS Boot Order to boot from {{ boot_device }} with next boot from {{ next_boot_device }}
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ boot_key: "DeviceName"
+ boot_device: "{{ boot_device }}"
+ next_boot_device: "{{ next_boot_device }}"
+ force_new: true
+ ignore_power_on: false
+ delegate_to: localhost
diff --git a/examples/set_bios_boot_order_default.yml b/examples/set_bios_boot_order_default.yml
new file mode 100644
index 0000000..c8c5469
--- /dev/null
+++ b/examples/set_bios_boot_order_default.yml
@@ -0,0 +1,39 @@
+---
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see [LICENSE.md](LICENSE.md) or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# example playbook to set BIOS boot order to default
+
+# Notes:
+# - this playbook will abort if the server is 'on'
+# - this playbook will cause the server to reboot a few times
+
+# variables not defined in this playbook are expected to be provided
+# elsewhere, e.g. in group_vars/all
+
+- name: set BIOS boot order to default
+ connection: local
+ hosts: iRMC_group
+
+ vars:
+ # iRMC login credentials
+ # irmc_user: "admin"
+ # irmc_password: "admin"
+ # Note: set validate_certificate to false for self-signed certificate
+ # validate_certificate: false
+
+ gather_facts: false
+
+ tasks:
+ # Set Bios Boot Order
+ - name: Set BIOS Boot Order to default
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "default"
+ force_new: true
+ ignore_power_on: false
+ delegate_to: localhost
diff --git a/group_vars/all b/group_vars/all
index 8f524a7..a9dd26b 100644
--- a/group_vars/all
+++ b/group_vars/all
@@ -37,3 +37,14 @@
# new_user: "newroot"
# new_password: "password"
+# boot_key: "DeviceName"
+# boot_device: "(Bus 02 Dev 00)PCI RAID Adapter"
+# next_boot_device: "HL-DT-ST DVDRAM GHB0N"
+
+# bios_filename: "D3289_09.08F_sdr03.73.bin"
+# irmc_filename: "D3289-A1x.R1.36.0.UPC"
+# tftp_server: "192.168.2.1"
+
+# irmc_sysconfig_file: "./SystemConfig.json"
+# irmc_hwconfig_file: "./HWConfiguration.json"
+
diff --git a/library/irmc_biosbootorder.py b/library/irmc_biosbootorder.py
new file mode 100644
index 0000000..854c6bc
--- /dev/null
+++ b/library/irmc_biosbootorder.py
@@ -0,0 +1,431 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_biosbootorder
+
+short_description: configure iRMC to force next boot to specified option
+
+description:
+ - Ansible module to configure the BIOS boot oder via iRMC.
+ - Using this module will force server into several reboots.
+ - The module will abort by default if the PRIMERGY server is powered on.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - The PRIMERGY server needs to be at least a M2 model.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: Get or set BIOS Boot Order.
+ required: false
+ default: get
+ choices: ['get', 'set', 'default']
+ ignore_power_on:
+ description: Ignore that server is powered on.
+ required: false
+ default: false
+ boot_key:
+ description: Which key to check for in bios boot order devices.
+ required: false
+ default: StructuredBootString
+ choices: ['DeviceName', 'StructuredBootString']
+ boot_device:
+ description: String to match with specified key for existing boot devices.
+ Needs to be provided for 'set' command.
+ required: false
+ force_new:
+ description: Force generation of new BiosBootOrder configuration in iRMC before getting or setting boot order.
+ default: false
+ required: false
+ next_boot_device:
+ description: Set next boot to specified device.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# Get Bios Boot Order
+- name: Get Bios Boot Order
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ force_new: false
+ delegate_to: localhost
+
+# Set Bios Boot Order to default
+- name: Get Bios Boot Order to default
+ irmc_biosbootorder:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "default"
+ ignore_power_on: false
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# boot_order returned for command "get":
+ DeviceIdx:
+ description: device index
+ returned: always
+ type: int
+ sample: 1
+ DeviceName:
+ description: device name
+ returned: always
+ type: string
+ sample: (Bus 01 Dev 00)PCI RAID Adapter
+ StructuredBootString:
+ description: structured boot string
+ returned: always
+ type: string
+ sample: RAID.Slot.1.Legacy
+
+# For all other commands:
+ Default return values:
+
+'''
+
+
+import json
+import copy
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_post, irmc_redfish_delete, get_irmc_json, \
+ waitForSessionToFinish
+from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, add_scci_command, \
+ scci_body_start, scci_body_end
+from ansible.module_utils.irmc_utils import compare_irmc_profile
+
+
+# Global
+result = dict()
+
+
+def irmc_biosbootorder(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ preliminary_parameter_check(module)
+
+ # check that all iRMC Profile processing states are terminated
+ waitForIrmcSessionsInactive(module)
+
+ # ToDo: Check that next_boot_device exists
+ # ToDo: Set *only* next_boot_device
+ if module.params['command'] == "default":
+ set_default_bootorder(module)
+ result['changed'] = True
+ module.exit_json(**result)
+
+ if module.params['force_new'] is True:
+ force_new_boot_profile(module)
+
+ # Get Boot Profile Data
+ boot_profile_data = get_boot_profile_data(module)
+ devices = get_irmc_json(boot_profile_data, ["Server", "SystemConfig", "BiosConfig", "BiosBootOrder", "Devices"])
+
+ if module.params['command'] == "get":
+ for key, devicelist in devices.items():
+ result['boot_order'] = []
+ for device in devicelist:
+ bo = {}
+ bo['DeviceIdx'] = device['@DeviceIdx']
+ bo['DeviceName'] = device['DeviceName']
+ bo['StructuredBootString'] = device['StructuredBootString']
+ result['boot_order'].append(bo)
+ module.exit_json(**result)
+
+ # setup new boot order
+ new_profile = setup_new_boot_profile(module, boot_profile_data)
+
+ comparison_list = []
+ comparison_result, comparison_list = compare_irmc_profile(boot_profile_data, new_profile, "", "", comparison_list)
+ if comparison_result is True:
+ result['skipped'] = True
+ result['msg'] = "Bios boot order is already as requested."
+ module.exit_json(**result)
+
+ # activate the new profile
+ new_profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['BootOrderApply'] = True
+ new_profile['Server']['SystemConfig']['BiosConfig']['@Processing'] = "execute"
+
+ # Set new boot profile
+ status, sysdata, msg = irmc_redfish_post(module, "rest/v1/Oem/eLCM/ProfileManagement/set", json.dumps(new_profile))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # check that current session is terminated
+ status, data, msg = waitForSessionToFinish(module, get_irmc_json(sysdata.json(), ["Session", "Id"]))
+ if status > 30 and status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, log=data, status=status)
+
+ result['changed'] = True
+ module.exit_json(**result)
+
+
+def get_boot_profile_data(module):
+ status, sysdata, msg = irmc_redfish_get(module, "rest/v1/Oem/eLCM/ProfileManagement/BiosBootOrder")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status == 404: # Boot Profile does not yet exist, create
+ result['msg'] = "Boot Profile does not yet exist. Create manually or restart with 'force_new' set to 'True'."
+ result['status'] = status
+ module.fail_json(**result)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ return sysdata.json()
+
+
+def preliminary_parameter_check(module):
+ if module.params['command'] == "set" and module.params['boot_device'] is None:
+ result['msg'] = "Command 'set' requires 'boot_device' parameter to be set!"
+ result['status'] = 10
+ module.fail_json(**result)
+
+ if module.params['command'] in ("set", "default") and module.params['ignore_power_on'] is False:
+ # Get server power state
+ status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ if get_irmc_json(sysdata.json(), "PowerState") == "On":
+ result['skipped'] = True
+ result['warnings'] = "Server is powered on. Cannot continue."
+ module.exit_json(**result)
+
+
+def setup_new_boot_profile(module, profile):
+ index = 0
+ boot_devices = ""
+ new_profile = copy.deepcopy(profile)
+ devices = get_irmc_json(new_profile, ["Server", "SystemConfig", "BiosConfig", "BiosBootOrder", "Devices"])
+
+ new_bootorder = []
+ for key, devicelist in devices.items():
+ for item in devicelist:
+ for ikey, value in item.items():
+ if ikey == module.params['boot_key'] and value == module.params['boot_device']:
+ index += 1
+ item['@DeviceIdx'] = index
+ if boot_devices != "":
+ boot_devices += ", "
+ boot_devices += item[module.params['boot_key']]
+ new_bootorder.append(item)
+ for item in devicelist:
+ for ikey, value in item.items():
+ if ikey == module.params['boot_key'] and value != module.params['boot_device']:
+ index += 1
+ item['@DeviceIdx'] = index
+ if boot_devices != "":
+ boot_devices += ", "
+ boot_devices += str(item[module.params['boot_key']])
+ new_bootorder.append(item)
+
+ if module.params['boot_device'] not in boot_devices:
+ msg = "'boot_device' '{0}' cannot be found in existing boot devices: '{1}'". \
+ format(module.params['boot_device'], boot_devices)
+ module.fail_json(msg=msg, status=20)
+
+ if module.params['next_boot_device'] is not None and module.params['next_boot_device'] not in boot_devices:
+ msg = "'next_boot_device' '{0}' cannot be found in existing boot devices: '{1}'". \
+ format(module.params['next_boot_device'], boot_devices)
+ module.fail_json(msg=msg, status=21)
+
+ # add new boot order to profile
+ del new_profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['Devices']['Device']
+ new_profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['Devices']['Device'] = new_bootorder
+ if module.params['next_boot_device'] is not None:
+ new_profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['NextBootDevice'] = \
+ module.params['next_boot_device']
+
+ return new_profile
+
+
+def force_new_boot_profile(module):
+ # check whether 'Automatic BiosParameter Backup' is set
+ scci_map = [ # Param, SCCI Name, SCCI Code, value
+ ["bios_backup_enabled", "ConfPermanentBiosConfigStorageEnabled", 0x1CC0, None],
+ ["bios_config_active", "ConfPermanentBiosConfigActive", 0x2721, None],
+ ]
+ datadict = dict()
+ datadict['bios_backup_enabled'] = None
+ datadict['bios_config_active'] = None
+
+ body = scci_body_start
+ for elem in scci_map:
+ body += add_scci_command("GET", scci_map, elem[1], 0, '')
+ body += scci_body_end
+
+ # send command list to scripting interface
+ status, data, msg = irmc_scci_post(module, body)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204, 404):
+ module.fail_json(msg=msg, status=status)
+
+ # evaluate results list
+ datadict, scciresult, sccicontext = get_scciresultlist(data.content, datadict, scci_map)
+ if scciresult != 0:
+ module.fail_json(msg=sccicontext, status=status)
+
+ # we only need to generate a new profile if 'Automatic BiosParameter Backup' is not set and active
+ if datadict['bios_backup_enabled'] == "0" or datadict['bios_config_active'] == "0":
+ # Delete current Boot Profile Data (if it exists)
+ status, sysdata, msg = irmc_redfish_delete(module, "rest/v1/Oem/eLCM/ProfileManagement/BiosBootOrder")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status not in (200, 202, 204, 404):
+ module.fail_json(msg=msg, status=status)
+
+ # Generate new Boot Profile Data
+ url = "/rest/v1/Oem/eLCM/ProfileManagement/get?PARAM_PATH=Server/SystemConfig/BiosConfig/BiosBootOrder"
+ status, sysdata, msg = irmc_redfish_post(module, url, "")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # check that current session is terminated
+ status, data, msg = waitForSessionToFinish(module, get_irmc_json(sysdata.json(), ["Session", "Id"]))
+ if status > 30 and status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, log=data, status=status)
+
+
+def waitForIrmcSessionsInactive(module):
+ # Get iRMC Profile processing state
+ status, sessiondata, msg = irmc_redfish_get(module, "sessionInformation")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sessiondata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ sessions = get_irmc_json(sessiondata.json(), ["SessionList"])
+ for key, session in sessions.items():
+ for item in session:
+ for ikey, value in item.items():
+ if ikey == "#text" and "Profile" in value:
+ status, sessiondata, msg = waitForSessionToFinish(module, item['@Id'])
+ continue
+
+
+def set_default_bootorder(module):
+ new_profile = {
+ "Server": {
+ "@Version": "1.01",
+ "SystemConfig": {
+ "BiosConfig": {
+ "@Processing": "execute",
+ "BiosBootOrder": {
+ "BootOrderApply": True,
+ "BootOrderReset": True
+ },
+ "@Version": "1.03"
+ }
+ }
+ }
+ }
+
+ if module.params['next_boot_device'] is not None:
+ new_profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['NextBootDevice'] = \
+ module.params['next_boot_device']
+
+ # Set new boot profile
+ status, data, msg = irmc_redfish_post(module, "rest/v1/Oem/eLCM/ProfileManagement/set", json.dumps(new_profile))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # check that current session is terminated
+ status, data, msg = waitForSessionToFinish(module, get_irmc_json(data.json(), ["Session", "Id"]))
+ if status > 30 and status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, log=data, status=status)
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="get", choices=['get', 'set', 'default']),
+ ignore_power_on=dict(required=False, type="bool", default=False),
+ boot_key=dict(required=False, type="str", default="StructuredBootString",
+ choices=['DeviceName', 'StructuredBootString']),
+ boot_device=dict(required=False, type="str"),
+ force_new=dict(required=False, type="bool", default=False),
+ next_boot_device=dict(required=False, type="str")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_biosbootorder(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_cas.py b/library/irmc_cas.py
new file mode 100644
index 0000000..9ecec9d
--- /dev/null
+++ b/library/irmc_cas.py
@@ -0,0 +1,336 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_cas
+
+short_description: manage iRMC CAS settings
+
+description:
+ - Ansible module to manage iRMC CAS settings via iRMC remote scripting interface.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: How to handle iRMC CAS data.
+ required: false
+ default: get
+ choices: ['get', 'set']
+ enabled:
+ description: CAS enabled.
+ required: false
+ ssl_verify:
+ description: Verify SSL Certificate.
+ required: false
+ login_always:
+ description: Always Display Login Page.
+ required: false
+ server:
+ description: CAS Server.
+ required: false
+ port:
+ description: CAS Port.
+ required: false
+ login_uri:
+ description: CAS Login URL.
+ required: false
+ logout_uri:
+ description: CAS Logout URL.
+ required: false
+ validate_uri:
+ description: CAS Validate URL.
+ required: false
+ privilege_level:
+ description: Privilege Level.
+ required: false
+ choices: ['Reserved', 'Callback', 'User', 'Operator', 'Administrator', 'OEM', 'NoAccess']
+ privilege_source:
+ description: Assign CAS permissions from.
+ required: false
+ choices: ['Local', 'LDAP']
+ privilege_user:
+ description: Configure User Accounts.
+ required: false
+ privilege_bmc:
+ description: Configure iRMC Settings.
+ required: false
+ privilege_avr:
+ description: Video Redirection Enabled.
+ required: false
+ privilege_storage:
+ description: Remote Storage Enable.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/12563/wp-svs-irmc-remote-scripting-en.pdf
+ - See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
+'''
+
+EXAMPLES = '''
+# Get CAS data
+- name: Get CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: cas
+ delegate_to: localhost
+- name: Show iRMC CAS data
+ debug:
+ msg: "{{ cas.cas }}"
+
+# Set CAS data
+- name: Set CAS data
+ irmc_cas:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ cas_user: "username"
+ cas_password: "password"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# CAS data returned by command "get":
+ enabled:
+ description: CAS enabled
+ returned: always
+ type: bool
+ sample: False
+ ssl_verify:
+ description: verify SSL Certificate
+ returned: always
+ type: bool
+ sample: True
+ login_always:
+ description: always Display Login Page
+ returned: always
+ type: bool
+ sample: True
+ server:
+ description: CAS server
+ returned: always
+ type: string
+ sample: cas_server.local
+ port:
+ description: CAS port
+ returned: always
+ type: int
+ sample: 3170
+ login_uri:
+ description: CAS Login URL
+ returned: always
+ type: string
+ sample: /cas/login
+ logout_uri:
+ description: CAS Logout URL
+ returned: always
+ type: string
+ sample: /cas/logout
+ validate_uri:
+ description: CAS Validate URL
+ returned: always
+ type: string
+ sample: /cas/validate
+ privilege_level:
+ description: privilege Level
+ returned: always
+ type: string
+ sample: Operator
+ privilege_source:
+ description: assign CAS permissions from
+ returned: always
+ type: string
+ sample: Local
+ privilege_user:
+ description: configure User Accounts
+ returned: always
+ type: bool
+ sample: False
+ privilege_bmc:
+ description: configure iRMC Settings
+ returned: always
+ type: bool
+ sample: False
+ privilege_avr:
+ description: Video Redirection Enabled
+ returned: always
+ type: bool
+ sample: False
+ privilege_storage:
+ description: Remote Storage Enable
+ returned: always
+ type: bool
+ sample: False
+
+# For command "set":
+ Default return values:
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, setup_datadict, \
+ setup_commandlist
+
+
+cas_priv = {"0": "Reserved", "1": "Callback", "2": "User", "3": "Operator", "4": "Administrator", "5": "OEM", "15": "NoAccess"}
+cas_priv_src = {"0": "Local", "1": "LDAP"}
+cas_login = {"0": "Automatic", "1": "Login page"}
+true_false = {"0": "False", "1": "True"}
+param_scci_map = [
+ # Param, SCCI Name, SCCI Code, index, value dict
+ ["enabled", "ConfBmcCasEnable", 0x1941, 0, true_false], # iRMC: CAS Enabled
+ ["ssl_verify", "ConfBmcCasVerifyServerCert", 0x1948, 0, true_false], # iRMC: Verify SSL Certificate
+ ["login_always", "ConfBmcCasAlwaysDisplayLogin", 0x194F, 0, cas_login], # iRMC: Always Display Login Page
+ ["server", "ConfBmcCasServer", 0x1942, 0, None], # iRMC: CAS Server
+ ["port", "ConfBmcCasPort", 0x1943, 0, None], # iRMC: CAS Port
+ ["login_uri", "ConfBmcCasLoginUri", 0x1944, 0, None], # iRMC: CAS Login URL
+ ["logout_uri", "ConfBmcCasLogoutUri", 0x1945, 0, None], # iRMC: CAS Logout URL
+ ["validate_uri", "ConfBmcCasValidateUri", 0x1946, 0, None], # iRMC: CAS Validate URL
+ ["privilege_level", "ConfBmcCasAssignConfiguredPermissions", 0x194E, 0, cas_priv], # iRMC: Privilege Level
+ ["privilege_source", "ConfBmcCasNetworkPrivilege", 0x1949, 0, cas_priv_src], # iRMC: Assign permissions from
+ ["privilege_user", "ConfBmcCasConfigureUsers", 0x194B, 0, true_false], # iRMC: Configure User Accounts
+ ["privilege_bmc", "ConfBmcCasPermissionConfigureBmc", 0x194A, 0, true_false], # iRMC: Configure iRMC Settings
+ ["privilege_avr", "ConfBmcCasPermissionAvrEnabled", 0x194C, 0, true_false], # iRMC: Video Redirection Enabled
+ ["privilege_storage", "ConfBmcCasRemoteStorageEnabled", 0x194D, 0, true_false], # iRMC: Remote Storage Enable
+]
+
+
+def irmc_cas(module):
+ result = dict(
+ changed=False,
+ status=0
+ )
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ casdata, setparam_count = setup_datadict(module)
+
+ # preliminary parameter check
+ if module.params['command'] == "set" and setparam_count == 0:
+ result['msg'] = "Command 'set' requires at least one parameter to be set!"
+ result['status'] = 10
+ module.fail_json(**result)
+
+ if module.params['command'] == "set":
+ body = setup_commandlist(casdata, "SET", param_scci_map)
+ else:
+ body = setup_commandlist(casdata, "GET", param_scci_map)
+
+ # send command list to scripting interface
+ status, data, msg = irmc_scci_post(module, body)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # evaluate results list
+ casdata, scciresult, sccicontext = get_scciresultlist(data.content, casdata, param_scci_map)
+ if scciresult != 0:
+ module.fail_json(msg=sccicontext, status=scciresult)
+
+ if module.params['command'] == "get":
+ result['cas'] = setup_resultdata(casdata)
+ else:
+ result['changed'] = True
+
+ module.exit_json(**result)
+
+
+def setup_resultdata(data):
+ data = {
+ 'enabled': true_false.get(data['enabled']),
+ 'ssl_verify': true_false.get(data['ssl_verify']),
+ 'login_always': cas_login.get(data['login_always']),
+ 'server': data['server'],
+ 'port': data['port'],
+ 'login_uri': data['login_uri'],
+ 'logout_uri': data['logout_uri'],
+ 'validate_uri': data['validate_uri'],
+ 'privilege_level': cas_priv.get(data['privilege_level']),
+ 'privilege_source': cas_priv_src.get(data['privilege_source']),
+ 'privilege_user': true_false.get(data['privilege_user']),
+ 'privilege_bmc': true_false.get(data['privilege_bmc']),
+ 'privilege_avr': true_false.get(data['privilege_avr']),
+ 'privilege_storage': true_false.get(data['privilege_storage']),
+ }
+ return data
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="get", choices=['get', 'set']),
+ enabled=dict(required=False, type="bool"),
+ ssl_verify=dict(required=False, type="bool"),
+ login_always=dict(required=False, type="str", choices=['Automatic', 'Login page']),
+ server=dict(required=False, type="str"),
+ port=dict(required=False, type="int"),
+ login_uri=dict(required=False, type="str"),
+ logout_uri=dict(required=False, type="str"),
+ validate_uri=dict(required=False, type="str"),
+ privilege_level=dict(required=False, type="str", choices=['Reserved', 'Callback', 'User', 'Operator', 'Administrator', 'OEM', 'NoAccess']),
+ privilege_source=dict(required=False, type="str", choices=['Local', 'LDAP']),
+ privilege_user=dict(required=False, type="bool"),
+ privilege_bmc=dict(required=False, type="bool"),
+ privilege_avr=dict(required=False, type="bool"),
+ privilege_storage=dict(required=False, type="bool"),
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_cas(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_certificate.py b/library/irmc_certificate.py
index cfd26bc..03c68e2 100644
--- a/library/irmc_certificate.py
+++ b/library/irmc_certificate.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,46 +22,47 @@
description:
- Ansible module to manage iRMC certificates via iRMC remote scripting interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: get or set iRMC certificate(s)
+ description: Get or set iRMC certificate(s).
required: false
default: get
choices: ['get', 'set']
private_key_path:
- description: path to file containing SSL private key;
- this option also requires the SSL certificate
+ description: Path to file containing SSL private key.
+ This option also requires the SSL certificate.
required: false
ssl_cert_path:
- description: path to file containing SSL certificate;
- this option also requires the SSL private key
+ description: Path to file containing SSL certificate.
+ This option also requires the SSL private key.
required: false
ssl_ca_cert_path:
- description: path to file containing SSL CA certificate
+ description: Path to file containing SSL CA certificate.
required: false
notes:
@@ -99,29 +100,38 @@
'''
RETURN = '''
-certificates:
- description: SSL certificates
- returned: always
- type: dict
+# Certificates returned by command "get":
+ ssl_certificate:
+ description: SSL certificate
+ returned: always
+ type: string
+ ssl_ca_certificate:
+ description: SSL CA certificate
+ returned: always
+ type: string
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
+from builtins import str
+
from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, add_scci_command, \
- scci_body_start, scci_body_end
+from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, setup_datadict, \
+ setup_commandlist
param_scci_map = [
- # Param, SCCI Name, SCCI Code, value dict
- ["private_key_path", "ConfBMCSslPrivateKey", 0x1981, None],
- ["ssl_cert_path", "ConfBMCSslCertificate", 0x1982, None],
- ["ssl_ca_cert_path", "ConfBMCSslCaCertificate", 0x1983, None],
+ # Param, SCCI Name, SCCI Code, index, value dict
+ ["private_key_path", "ConfBMCSslPrivateKey", 0x1981, 0, None],
+ ["ssl_cert_path", "ConfBMCSslCertificate", 0x1982, 0, None],
+ ["ssl_ca_cert_path", "ConfBMCSslCaCertificate", 0x1983, 0, None],
]
-def irmc_certificate(module): # pylint: disable=too-many-branches
+def irmc_certificate(module):
result = dict(
changed=False,
status=0
@@ -131,29 +141,31 @@ def irmc_certificate(module): # pylint: disable=too-many-branches
result['msg'] = "module was not run"
module.exit_json(**result)
- certdata, setparam_count = setup_datadict(module)
+ certdata, setparam_count = setup_datadict(module, False)
# parameter check
if module.params['command'] == "set":
if setparam_count == 0:
result['msg'] = "Command 'set' requires at least one parameter to be set!"
+ result['status'] = 10
module.fail_json(**result)
if certdata['private_key_path'] is not None and certdata['ssl_cert_path'] is None or \
certdata['ssl_cert_path'] is not None and certdata['private_key_path'] is None:
result['msg'] = "Both 'private_key_path' and 'ssl_cert_path' are required to successfully " + \
"import SSL key pair!"
+ result['status'] = 11
module.fail_json(**result)
certdata, status, msg = read_keyfile(certdata, 'private_key_path')
if status != 0:
- module.fail_json(msg=msg)
+ module.fail_json(msg=msg, status=status)
certdata, status, msg = read_keyfile(certdata, 'ssl_cert_path')
if status != 0:
- module.fail_json(msg=msg)
+ module.fail_json(msg=msg, status=status)
certdata, status, msg = read_keyfile(certdata, 'ssl_ca_cert_path')
if status != 0:
- module.fail_json(msg=msg)
+ module.fail_json(msg=msg, status=status)
if module.params['command'] == "set":
body = setup_commandlist(certdata, "SET", param_scci_map)
@@ -163,15 +175,14 @@ def irmc_certificate(module): # pylint: disable=too-many-branches
# send command list to scripting interface
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
# evaluate results list
certdata, scciresult, sccicontext = get_scciresultlist(data.content, certdata, param_scci_map)
if scciresult != 0:
- result['msg'] = sccicontext
- module.fail_json(**result)
+ module.fail_json(msg=sccicontext, status=scciresult)
if module.params['command'] == "get":
result['certificates'] = setup_resultdata(certdata)
@@ -181,23 +192,6 @@ def irmc_certificate(module): # pylint: disable=too-many-branches
module.exit_json(**result)
-# we need our own function here as empty values are not allowed
-def setup_datadict(module):
- spcount = 0
- datadict = dict()
- for key, value in module.params.iteritems():
- if key != "irmc_url" and key != "irmc_username" and key != "irmc_password" and \
- key != "validate_certs" and key != "command":
- if value is not None:
- if value != "":
- spcount += 1
- else:
- value = None
- datadict[key] = value
-
- return datadict, spcount
-
-
def read_keyfile(data, param):
context = cert = ""
result = 0
@@ -205,7 +199,7 @@ def read_keyfile(data, param):
try:
f = open(data[param], 'r')
cert = f.read()
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
result = 89
context = "Could not read key/certificate file at '{0}': {1}".format(data[param], str(e))
data[param] = cert
@@ -213,14 +207,6 @@ def read_keyfile(data, param):
return data, result, context
-def setup_commandlist(cmdlist, ctype, scci_map):
- body = scci_body_start
- for elem in scci_map:
- body += add_scci_command(ctype, scci_map, elem[1], 0, cmdlist[elem[0]])
- body += scci_body_end
- return body
-
-
def setup_resultdata(data):
data = {
'ssl_certificate': data['ssl_cert_path'],
diff --git a/library/irmc_compare_profiles.py b/library/irmc_compare_profiles.py
new file mode 100644
index 0000000..650c7f2
--- /dev/null
+++ b/library/irmc_compare_profiles.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_compare_profiles
+
+short_description: compare two iRMC profiles
+
+description:
+ - Ansible module to compare two iRMC profiles.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ profile_json1:
+ description: iRMC profile to be compared against another.
+ Takes precedence over profile_path1 when set.
+ required: false
+ profile_json2:
+ description: iRMC profile to be compared against another.
+ Takes precedence over profile_path2 when set.
+ required: false
+ profile_path1:
+ description: Path to file with iRMC profile to be compared against another.
+ Ignored if profile1 is set.
+ required: false
+ profile_path2:
+ description: Path to file with iRMC profile to be compared against another.
+ Ignored if profile2 is set.
+ required: false
+'''
+
+EXAMPLES = '''
+# Compare iRMC profiles against each other
+- name: Compare iRMC profiles
+ irmc_compare_profiles:
+ profile_path1: "{{ profile1_path }}"
+ profile_path2: "{{ profile2_path }}"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+ comparison_result:
+ description: profile comparison result
+ returned: always
+ type: bool
+ sample: False
+ comparison_list:
+ description: rudimentary list of probable comparison differences
+ returned: when comparison_result is False
+ type: list
+'''
+
+
+from builtins import str
+
+import json
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc_utils import compare_irmc_profile
+
+
+def irmc_compare_profiles(module):
+ result = dict(
+ changed=False,
+ status=0
+ )
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ if module.params['profile_path1'] is None and module.params['profile_json1'] is None:
+ module.fail_json(msg="Either 'profile_json1' or 'profile_path1' needs to be set.", status=10)
+ if module.params['profile_path2'] is None and module.params['profile_json2'] is None:
+ module.fail_json(msg="Either 'profile_json2' or 'profile_path2' needs to be set.", status=11)
+
+ if module.params['profile_json1'] is not None:
+ try:
+ profile1 = json.loads(module.params['profile_json1'])
+ except ValueError as e:
+ module.fail_json(msg="'profile_json1' is invalid JSON: {0}".
+ format(module.params['profile_json1']), status=12)
+ else:
+ try:
+ with open(module.params['profile_path1']) as profile1_str:
+ profile1 = json.load(profile1_str)
+ except Exception as e:
+ module.fail_json(msg="Could not read 'profile_path1' at '{0}': {1}".
+ format(module.params['profile_path1'], str(e)), status=13)
+
+ if module.params['profile_json2'] is not None:
+ try:
+ profile2 = json.loads(module.params['profile_json2'])
+ except ValueError as e:
+ module.fail_json(msg="'profile_json2' is invalid JSON: {0}".
+ format(module.params['profile_json2']), status=14)
+ else:
+ try:
+ with open(module.params['profile_path2']) as profile2_str:
+ profile2 = json.load(profile2_str)
+ except Exception as e:
+ module.fail_json(msg="Could not read 'profile_path2 at '{0}': {1}".
+ format(module.params['profile_path2'], str(e)), status=15)
+
+ comparison_result, comparison_list = compare_irmc_profile(profile1, profile2)
+ result['comparison_result'] = comparison_result
+ if comparison_result is False:
+ result['comparison_list'] = comparison_list
+
+ module.exit_json(**result)
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ profile_json1=dict(required=False, type="str"),
+ profile_json2=dict(required=False, type="str"),
+ profile_path1=dict(required=False, type="str"),
+ profile_path2=dict(required=False, type="str")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_compare_profiles(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_connectvm.py b/library/irmc_connectvm.py
index a3dfa9d..9b8ba72 100644
--- a/library/irmc_connectvm.py
+++ b/library/irmc_connectvm.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,34 +22,35 @@
description:
- Ansible module to connect iRMC Virtual Media Data via the iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: the virtual media connect command to be executed
+ description: The virtual media connect command to be executed.
required: false
default: ConnectCD
choices: ['ConnectCD', 'ConnectFD', 'ConnectHD', 'DisconnectCD', 'DisconnectFD', 'DisconnectHD']
@@ -82,14 +83,10 @@
'''
RETURN = '''
-result:
- description: connection action result
- returned: always
- type: dict
+Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
@@ -109,7 +106,7 @@ def irmc_connectvirtualmedia(module):
# Get iRMC system data
status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
+ module.fail_json(msg=msg, status=status, exception=sysdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -137,7 +134,7 @@ def irmc_connectvirtualmedia(module):
# Get iRMC Virtual Media data
status, vmdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/VirtualMedia/")
if status < 100:
- module.fail_json(msg=msg, exception=vmdata)
+ module.fail_json(msg=msg, status=status, xception=vmdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -145,6 +142,7 @@ def irmc_connectvirtualmedia(module):
remotemountenabled = get_irmc_json(vmdata.json(), "RemoteMountEnabled")
if not remotemountenabled:
result['msg'] = "Remote Mount of Virtual Media is not enabled!"
+ result['status'] = 20
module.fail_json(**result)
# Set iRMC system data
@@ -152,8 +150,8 @@ def irmc_connectvirtualmedia(module):
status, sysdata, msg = irmc_redfish_post(module, "redfish/v1/Systems/0/Actions/Oem/FTSComputerSystem.VirtualMedia",
json.dumps(body))
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
result['changed'] = True
diff --git a/library/irmc_eventlog.py b/library/irmc_eventlog.py
new file mode 100644
index 0000000..a2c79ac
--- /dev/null
+++ b/library/irmc_eventlog.py
@@ -0,0 +1,255 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_eventlog
+
+short_description: handle iRMC eventlogs
+
+description:
+ - Ansible module to handle iRMC eventlogs via Restful API.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: Handle iRMC eventlogs.
+ required: false
+ default: list
+ choices: ['list', 'get', 'clear']
+ eventlog_type:
+ description: Specific eventlog to handle.
+ default: SystemEventLog
+ required: false
+ choices: ['SystemEventLog', 'InternalEventLog']
+ id:
+ description: Specific eventlog ID to get.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# List iRMC InternalEventLog
+- name: List iRMC InternalEventLog
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ eventlog_type: "InternalEventLog"
+ delegate_to: localhost
+
+# Get specific SystemEventLog entry information
+- name: Get specific SystemEventLog entry information
+ irmc_eventlog:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 0
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# eventlog_entry data returned for command "get":
+ AlertGroup:
+ description: group which caused the event
+ returned: always
+ type: string
+ sample: Memory
+ Cause:
+ description: reason for the event if available
+ returned: always
+ type: string
+ sample: The memory module was not approved and released for this system by the system manufacturer
+ Created:
+ description: dated of the event
+ returned: always
+ type: string
+ sample: "2018-07-24T15:57:40+02:00"
+ EventSource:
+ description: where the event originated from
+ returned: always
+ type: string
+ sample: iRMC S5
+ Id:
+ description: event ID
+ returned: always
+ type: int
+ sample: 20
+ Message:
+ description: event entry text
+ returned: always
+ type: string
+ sample: DIMM-1E Non Fujitsu Memory Module detected - Warranty restricted!
+ Resolutions:
+ description: list of possible solitions for the problem, if available
+ returned: always
+ type: list
+ sample:
+ Severity:
+ description: serverity of event
+ returned: always
+ type: string
+ sample: Warning
+ Type:
+ description: event type
+ returned: always
+ type: string
+ sample: SEL
+
+# eventlog data returned for command "list":
+ List of individual eventlog_entries (see above):
+
+# For all other commands:
+ Default return values:
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_post, get_irmc_json
+
+
+# Global
+result = dict()
+
+
+def irmc_eventlog(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ if module.params['command'] == "get" and module.params['id'] is None:
+ result['msg'] = "Command 'get' requires 'id' parameter to be set!"
+ result['status'] = 10
+ module.fail_json(**result)
+
+ if module.params['command'] == "list":
+ status, data, msg = irmc_redfish_get(module, "redfish/v1/Managers/iRMC/LogServices/{0}/Entries".
+ format(module.params['eventlog_type']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ eventlogs = get_irmc_json(data.json(), ["Members"])
+ result['eventlog'] = []
+ for item in eventlogs:
+ result['eventlog'].append(get_irmc_eventlog_info(module, item))
+ elif module.params['command'] == "clear":
+ url = "redfish/v1/Managers/iRMC/LogServices/{0}/Actions/LogService.ClearLog". \
+ format(module.params['eventlog_type'])
+ status, data, msg = irmc_redfish_post(module, url, "")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ result['changed'] = True
+ else:
+ status, data, msg = irmc_redfish_get(module, "redfish/v1/Managers/iRMC/LogServices/{0}/Entries/{1}".
+ format(module.params['eventlog_type'], module.params['id']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ result['eventlog_entry'] = get_irmc_eventlog_info(module, data.json())
+
+ module.exit_json(**result)
+
+
+def get_irmc_eventlog_info(module, item):
+ eventlog = {}
+ eventlog['Id'] = item['Id']
+ eventlog['Severity'] = item['Severity']
+ eventlog['Created'] = item['Created']
+ eventlog['Type'] = item['EntryType']
+ eventlog['AlertGroup'] = get_irmc_json(item, ["Oem", "ts_fujitsu", "AlertGroup"])
+ if module.params['eventlog_type'] == "SystemEventLog":
+ eventlog['EventSource'] = get_irmc_json(item, ["Oem", "ts_fujitsu", "EventSource"])
+ eventlog['Message'] = get_irmc_json(item, ["Oem", "ts_fujitsu", "MessageOEM", "en"])[0]
+ if get_irmc_json(item, ["Oem", "ts_fujitsu", "Cause"]) is not None:
+ eventlog['Cause'] = get_irmc_json(item, ["Oem", "ts_fujitsu", "Cause", "en"])[0]
+ else:
+ eventlog['Cause'] = None
+ if get_irmc_json(item, ["Oem", "ts_fujitsu", "Resolutions"]) is not None:
+ eventlog['Resolutions'] = get_irmc_json(item, ["Oem", "ts_fujitsu", "Resolutions", "en"])[0]
+ else:
+ eventlog['Resolutions'] = None
+ else:
+ eventlog['MessageId'] = item['MessageId']
+ eventlog['Message'] = item['Message']
+ return eventlog
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="list",
+ choices=['list', 'get', 'clear']),
+ eventlog_type=dict(required=False, type="str", default="SystemEventLog",
+ choices=["SystemEventLog", "InternalEventLog"]),
+ id=dict(required=False, type="int")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_eventlog(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_facts.py b/library/irmc_facts.py
index 0ea222c..d9fdb12 100644
--- a/library/irmc_facts.py
+++ b/library/irmc_facts.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,48 +22,52 @@
description:
- Ansible module to get or set basic iRMC and PRIMERGY server data via iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: get or set server facts
+ description: Get or set server facts.
required: false
default: get
choices: ['get', 'set']
asset_tag:
- description: server asset tag
+ description: Server asset tag.
required: false
location:
- description: server location
+ description: Server location.
required: false
description:
- description: server description
+ description: Server description.
+ required: false
+ contact:
+ description: System contact.
required: false
helpdesk_message:
- description: help desk message
+ description: Help desk message.
required: false
notes:
@@ -99,19 +103,176 @@
'''
RETURN = '''
-facts:
- description: basic server and iRMC facts
- returned: always
- type: dict
+# facts returned by command "get":
+ hardware.ethernetinterfaces:
+ description:
+ dict with total number (count)
+ and list of ethernet interfaces (devices)
+ with relevant data (id, macaddress, name)
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 2,
+ "devices": [
+ { "id": "0", "macaddress": "01:02:03:04:05:06", "name": "eth0" },
+ { "id": "1", "macaddress": "01:02:03:04:05:06", "name": "eth1" }
+ ]
+ }
+ hardware.fans:
+ description:
+ dict with available fan slots (sockets)
+ and total number (count)
+ and list of existing fans (devices)
+ with relevant data (id, manufacturer, name, size)
+ note that fan devices are only returned if server is 'On'
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 2,
+ "devices": [
+ { "id": "0", "manufacturer": "Micron Technology", "name": "DIMM-1A", "size": 8192 },
+ { "id": "12", "manufacturer": "SK Hynix", "name": "DIMM-1E", "size": 16384 }
+ ],
+ "sockets": 24
+ }
+ hardware.memory:
+ description:
+ dict with available memory slots (sockets)
+ and total number (count)
+ and list of existing memories (devices)
+ with relevant data (id, manufacturer, name, size)
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 6,
+ "devices": [
+ { "id": "0", "location": "SystemBoard", "name": "FAN1 SYS" },
+ { "id": "1", "location": "SystemBoard", "name": "FAN2 SYS" },
+ { "id": "2", "location": "SystemBoard", "name": "FAN3 SYS" },
+ { "id": "3", "location": "SystemBoard", "name": "FAN4 SYS" },
+ { "id": "4", "location": "SystemBoard", "name": "FAN5 SYS" },
+ { "id": "5", "location": "PowerSupply", "name": "FAN PSU1" }
+ ],
+ "sockets": 7
+ }
+ hardware.powersupplies:
+ description:
+ dict with available power supply slots (sockets)
+ and total number (count)
+ and list of existing power supplies (devices)
+ with relevant data (id, manufacturer, model, name)
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 1,
+ "devices": [
+ { "id": "0", "manufacturer": "CHICONY", "model": "S13-450P1A", "name": "PSU1" }
+ ],
+ "sockets": 2
+ }
+ hardware.processors:
+ description:
+ dict with available processor slots (sockets)
+ and total number (count)
+ and list of existing processors (devices)
+ with relevant data (cores, id, name, threads)
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 2,
+ "devices": [
+ { "cores": 6, "id": "0", "name": "Genuine Intel(R) CPU @ 2.00GHz", "threads": 6 },
+ { "cores": 6, "id": "1", "name": "Genuine Intel(R) CPU @ 2.00GHz", "threads": 6 }
+ ],
+ "sockets": 2
+ }
+ hardware.storagecontrollers:
+ description:
+ dict with total number (count)
+ and list of storage controllers (devices)
+ with relevant data (drives, firmware, id, name, volume)
+ note that storage controllers are only returned if server is 'On'
+ returned: always
+ type: dict
+ sample:
+ {
+ "count": 1,
+ "devices": [
+ { "drives": 4, "firmware": "4.270.00-4869", "id": "1000", "name": "PRAID EP400i", "volumes": 1 }
+ ]
+ }
+ irmc:
+ description:
+ dict with relevant iRMC data
+ (fw_builddate, fw_running, fw_version, hostname, macaddress, sdrr_version)
+ returned: always
+ type: dict
+ sample:
+ {
+ "fw_builddate": "2018-03-05T14:02:44",
+ "fw_running": "LowFWImage",
+ "fw_version": "9.08F",
+ "hostname": "iRMC01CA5C-iRMC",
+ "macaddress": "90:1B:0E:01:CA:5C",
+ "sdrr_version": "3.73"
+ }
+ mainboard:
+ description:
+ dict with relevant mainboard data
+ (dnumber, manufacturer, part_number, serial_number, version)
+ returned: always
+ type: dict
+ sample:
+ {
+ "dnumber": "D3289",
+ "manufacturer": "FUJITSU",
+ "part_number": "S26361-D3289-D13",
+ "serial_number": "44617895",
+ "version": "WGS04 GS50"
+ }
+ system:
+ description:
+ dict with relevant system data
+ (asset_tag, bios_version, description, health, helpdesk_message, host_name,
+ idled_state, ip, location, manufacturer, memory_size, model, part_number,
+ power_state, serial_number, uuid)
+ returned: always
+ type: dict
+ sample:
+ {
+ "asset_tag": "New AssetTag",
+ "bios_version": "V5.0.0.9 R1.36.0 for D3289-A1x",
+ "contact": "Admin (admin@server.room)",
+ "description": "server description",
+ "health": "OK",
+ "helpdesk_message": "New helpdesk message",
+ "host_name": "STK-SLES11SP4x64",
+ "idled_state": "Off",
+ "ip": 101.102.103.104,
+ "location": "Server Room",
+ "manufacturer": "FUJITSU",
+ "memory_size": "24 GB",
+ "model": "PRIMERGY RX2540 M1",
+ "part_number": "ABN:K1495-VXXX-XX",
+ "power_state": "On",
+ "serial_number": "YLVT000098",
+ "uuid": "11223344-5566-cafe-babe-deadbeef1234"
+ }
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_patch, get_irmc_json
-from ansible.module_utils.irmc_scci_utils import setup_datadict
def irmc_facts(module):
@@ -124,42 +285,161 @@ def irmc_facts(module):
result['msg'] = "module was not run"
module.exit_json(**result)
- facts, count = setup_datadict(module) # pylint: disable=unused-variable
-
- # Get iRMC system data
- status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
- if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
- elif status != 200:
- module.fail_json(msg=msg, status=status)
+ # parameter check
+ if module.params['command'] == "set" and \
+ module.params['asset_tag'] is None and module.params['description'] is None and \
+ module.params['helpdesk_message'] is None and module.params['location'] is None and \
+ module.params['contact'] is None:
+ result['msg'] = "Command 'set' requires at least one parameter to be set!"
+ result['status'] = 10
+ module.fail_json(**result)
# Get iRMC OEM system data
status, oemdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/System")
if status < 100:
- module.fail_json(msg=msg, exception=oemdata)
+ module.fail_json(msg=msg, status=status, exception=oemdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
if module.params['command'] == "get":
- result['facts'] = setup_resultdata(sysdata, oemdata)
- module.exit_json(**result)
+ # Get iRMC system data
+ status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+ power_state = get_irmc_json(sysdata.json(), "PowerState")
- # Set iRMC OEM system data
- if module.params['asset_tag'] is not None or module.params['location'] is not None or \
- module.params['description'] is not None or module.params['helpdesk_message'] is not None:
- body = setup_facts(facts)
- etag = get_irmc_json(oemdata.json(), "@odata.etag")
- status, patch, msg = irmc_redfish_patch(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/System/",
- json.dumps(body), etag)
+ # Get iRMC FW data
+ status, fwdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/FirmwareInventory")
if status < 100:
- module.fail_json(msg=msg, exception=patch)
+ module.fail_json(msg=msg, status=status, exception=fwdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
- result['changed'] = True
+ result['facts'] = setup_resultdata(sysdata, oemdata, fwdata)
+ result = add_system_hw_info(power_state, module, result)
+ result = add_chassis_hw_info(module, result)
+ result = add_irmc_hw_info(module, result)
+ module.exit_json(**result)
+
+ # Set iRMC OEM system data
+ body = setup_facts(module.params)
+ etag = get_irmc_json(oemdata.json(), "@odata.etag")
+ status, patch, msg = irmc_redfish_patch(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/System/",
+ json.dumps(body), etag)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=patch)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ result['changed'] = True
module.exit_json(**result)
+def add_system_hw_info(power_state, module, result):
+ # get system hardware
+ for hw in ("Memory", "Processors", "EthernetInterfaces", "Storage"):
+ status, hwdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/{0}?$expand=Members".format(hw))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=hwdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+ items = 0
+ hw_dict = {}
+ hw_dict['devices'] = []
+ for member in get_irmc_json(hwdata.json(), "Members"):
+ hw_list = {}
+ if get_irmc_json(member, ["Status", "State"]) == "Enabled":
+ if hw == "Memory":
+ hw_list['id'] = get_irmc_json(member, ["Id"])
+ hw_list['name'] = get_irmc_json(member, ["DeviceLocator"])
+ hw_list['manufacturer'] = get_irmc_json(member, ["Manufacturer"])
+ hw_list['size'] = get_irmc_json(member, ["CapacityMiB"])
+ if hw == "Processors":
+ hw_list['id'] = get_irmc_json(member, ["Id"])
+ hw_list['name'] = get_irmc_json(member, ["Model"])
+ hw_list['cores'] = get_irmc_json(member, ["TotalCores"])
+ hw_list['threads'] = get_irmc_json(member, ["TotalThreads"])
+ if hw == "EthernetInterfaces":
+ hw_list['id'] = get_irmc_json(member, ["Id"])
+ hw_list['name'] = get_irmc_json(member, ["Description"])
+ if "does not exist" in hw_list['name']:
+ hw_list['name'] = "{0} {1}".format(get_irmc_json(member, ["Name"]), hw_list['id'])
+ hw_list['macaddress'] = get_irmc_json(member, ["MACAddress"])
+ if hw == "Storage" and power_state == "On":
+ # iRMC has each StroageController with its own Storage
+ for ctrl in get_irmc_json(member, "StorageControllers"):
+ hw_list['id'] = get_irmc_json(member, ["Id"])
+ hw_list['name'] = get_irmc_json(ctrl, ["Model"])
+ hw_list['firmware'] = get_irmc_json(ctrl, ["FirmwareVersion"])
+ hw_list['drives'] = get_irmc_json(ctrl, ["Oem", "ts_fujitsu", "DriveCount"])
+ hw_list['volumes'] = get_irmc_json(ctrl, ["Oem", "ts_fujitsu", "VolumeCount"])
+ items += 1
+ if hw_list:
+ hw_dict['devices'].append(hw_list)
+ hw_dict['count'] = items
+ if hw == "Storage":
+ hw = "StorageControllers"
+ if hw in ("Memory", "Processors"):
+ hw_dict['sockets'] = get_irmc_json(hwdata.json(), "Members@odata.count")
+ result['facts']['hardware'][hw.lower()] = hw_dict
+ return result
+
+
+def add_chassis_hw_info(module, result):
+ # get chassis hardware
+ hw_source = {
+ "redfish/v1/Chassis/0/Thermal#/Fans": ["Fans"],
+ "redfish/v1/Chassis/0/Power#/PowerSupplies": ["PowerSupplies"],
+ }
+ for hw_link, hw_list in hw_source.items():
+ status, hwdata, msg = irmc_redfish_get(module, hw_link)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=hwdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+ for hw in hw_list:
+ items = 0
+ hw_dict = {}
+ hw_dict['devices'] = []
+ for member in get_irmc_json(hwdata.json(), hw):
+ hw_list = {}
+ if get_irmc_json(member, ["Status", "State"]) == "Enabled":
+ if hw == "PowerSupplies":
+ hw_list['id'] = get_irmc_json(member, ["MemberId"])
+ hw_list['name'] = get_irmc_json(member, ["Name"])
+ hw_list['manufacturer'] = get_irmc_json(member, ["Manufacturer"])
+ hw_list['model'] = get_irmc_json(member, ["Model"])
+ elif hw == "Voltages":
+ hw_list['id'] = get_irmc_json(member, ["MemberId"])
+ hw_list['name'] = get_irmc_json(member, ["Name"])
+ else:
+ hw_list['id'] = get_irmc_json(member, ["MemberId"])
+ hw_list['name'] = get_irmc_json(member, ["Name"])
+ hw_list['location'] = get_irmc_json(member, ["PhysicalContext"])
+ items += 1
+ if hw_list:
+ hw_dict['devices'].append(hw_list)
+ hw_dict['count'] = items
+ hw_dict['sockets'] = get_irmc_json(hwdata.json(), "{0}@odata.count".format(hw))
+ result['facts']['hardware'][hw.lower()] = hw_dict
+ return result
+
+
+def add_irmc_hw_info(module, result):
+ # get iRMC info
+ status, hwdata, msg = irmc_redfish_get(module, "redfish/v1/Managers/iRMC/EthernetInterfaces?$expand=Members")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=hwdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+ for member in get_irmc_json(hwdata.json(), "Members"):
+ result['facts']['irmc']['macaddress'] = "{0}".format(get_irmc_json(member, ["MACAddress"]))
+ result['facts']['irmc']['hostname'] = "{0}".format(get_irmc_json(member, ["HostName"]))
+ return result
+
+
def setup_facts(data):
body = dict()
if data['asset_tag'] is not None:
@@ -168,33 +448,50 @@ def setup_facts(data):
body['Location'] = data['location']
if data['description'] is not None:
body['Description'] = data['description']
+ if data['contact'] is not None:
+ body['Contact'] = data['contact']
if data['helpdesk_message'] is not None:
body['HelpdeskMessage'] = data['helpdesk_message']
return body
-def setup_resultdata(data, data2):
+def setup_resultdata(data, data2, data3):
data = {
- 'asset_tag': get_irmc_json(data2.json(), "AssetTag"),
- 'bios_version': get_irmc_json(data.json(), "BiosVersion"),
- 'idled_state': get_irmc_json(data.json(), "IndicatorLED"),
- 'host_name': get_irmc_json(data.json(), "HostName"),
- 'system_manufacturer': get_irmc_json(data.json(), "Manufacturer"),
- 'system_model': get_irmc_json(data.json(), "Model"),
- # 'system_name': get_irmc_json(data.json(), "Name"),
- 'system_part_number': get_irmc_json(data.json(), "PartNumber"),
- 'power_state': get_irmc_json(data.json(), "PowerState"),
- 'system_serial_number': get_irmc_json(data.json(), "SerialNumber"),
- 'system_uuid': get_irmc_json(data.json(), "UUID"),
- 'location': get_irmc_json(data2.json(), "Location"),
- 'description': get_irmc_json(data2.json(), "Description"),
- 'helpdesk_message': get_irmc_json(data2.json(), "HelpdeskMessage"),
- 'system_ip': get_irmc_json(data2.json(), "SystemIP"),
- 'mainboard_manufacturer': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Manufacturer"]),
- 'mainboard_dnumber': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Model"]),
- 'mainboard_part_number': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "PartNumber"]),
- 'mainboard_serial_number': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "SerialNumber"]),
- 'mainboard_version': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Version"])
+ 'system': {
+ 'bios_version': get_irmc_json(data.json(), "BiosVersion"),
+ 'idled_state': get_irmc_json(data.json(), "IndicatorLED"),
+ 'asset_tag': get_irmc_json(data2.json(), "AssetTag"),
+ 'host_name': get_irmc_json(data.json(), "HostName"),
+ 'manufacturer': get_irmc_json(data.json(), "Manufacturer"),
+ 'model': get_irmc_json(data.json(), "Model"),
+ # 'name': get_irmc_json(data.json(), "Name"),
+ 'part_number': get_irmc_json(data.json(), "PartNumber"),
+ 'serial_number': get_irmc_json(data.json(), "SerialNumber"),
+ 'uuid': get_irmc_json(data.json(), "UUID"),
+ 'ip': get_irmc_json(data2.json(), "SystemIP"),
+ 'location': get_irmc_json(data2.json(), "Location"),
+ 'description': get_irmc_json(data2.json(), "Description"),
+ 'contact': get_irmc_json(data2.json(), "Contact"),
+ 'helpdesk_message': get_irmc_json(data2.json(), "HelpdeskMessage"),
+ 'power_state': get_irmc_json(data.json(), "PowerState"),
+ 'memory_size': "{0} GB".format(get_irmc_json(data.json(), ["MemorySummary", "TotalSystemMemoryGiB"])),
+ 'health': get_irmc_json(data.json(), ["Status", "HealthRollup"]),
+ },
+ 'mainboard': {
+ 'manufacturer': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Manufacturer"]),
+ 'dnumber': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Model"]),
+ 'part_number': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "PartNumber"]),
+ 'serial_number': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "SerialNumber"]),
+ 'version': get_irmc_json(data.json(), ["Oem", "ts_fujitsu", "MainBoard", "Version"]),
+ },
+ 'irmc': {
+ 'fw_version': get_irmc_json(data3.json(), "BMCFirmware"),
+ 'fw_builddate': get_irmc_json(data3.json(), "BMCFirmwareBuildDate"),
+ 'fw_running': get_irmc_json(data3.json(), "BMCFirmwareRunning"),
+ 'sdrr_version': get_irmc_json(data3.json(), "SDRRVersion"),
+ },
+ 'hardware': {
+ },
}
return data
@@ -210,6 +507,7 @@ def main():
asset_tag=dict(required=False, type="str"),
location=dict(required=False, type="str"),
description=dict(required=False, type="str"),
+ contact=dict(required=False, type="str"),
helpdesk_message=dict(required=False, type="str"),
)
module = AnsibleModule(
diff --git a/library/irmc_fwbios_update.py b/library/irmc_fwbios_update.py
new file mode 100644
index 0000000..897a2e5
--- /dev/null
+++ b/library/irmc_fwbios_update.py
@@ -0,0 +1,473 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_fwbios_update
+
+short_description: update iRMC Firmware or server BIOS
+
+description:
+ - Ansible module to get current iRMC update settings or update iRMC Firmware or BIOS via iRMC RedFish interface.
+ - BIOS or firmware flash can be initiated from TFTP server or local file.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+ - python module 'requests_toolbelt'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: Get settings or run update.
+ required: false
+ default: get
+ choices: ['get', 'update']
+ ignore_power_on:
+ description: Ignore that server is powered on.
+ required: false
+ default: false
+ update_source:
+ description: Where to get the FW or BIOS update file.
+ required: false
+ choices: ['tftp', 'file']
+ update_type:
+ description: Whether to update iRMC FW or server BIOS.
+ required: false
+ choices: ['irmc', 'bios']
+ server_name:
+ description: TFTP server name or IP.
+ ignored if update_source is set to 'file'
+ required: false
+ file_name:
+ description: Path to file containing correct iRMC FW or server BIOS image.
+ required: false
+ irmc_flash_selector:
+ description: Which iRMC image to replace with the new firmware.
+ required: false
+ choices: ['Auto', 'LowFWImage', 'HighFWImage']
+ irmc_boot_selector:
+ description: Which iRMC FW image is to be started after iRMC reboot.
+ required: false
+ choices: ['Auto', 'LowFWImage', 'HighFWImage']
+
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# Get irmc firmware and BIOS update settings
+- name: Get irmc firmware and BIOS update settings
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: fw_settings
+ delegate_to: localhost
+- name: Show irmc firmware and BIOS update settings
+ debug:
+ msg: "{{ fw_settings.fw_update_configuration }}"
+
+# Update server BIOS from local file
+- name: Update server BIOS from local file
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "file"
+ update_type: "bios"
+ file_name: "{{ bios_filename }}"
+ delegate_to: localhost
+
+# Update iRMC FW
+- name: Update iRMC FW via TFTP
+ irmc_fwbios_update:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "update"
+ update_source: "tftp"
+ update_type: "irmc"
+ server_name: "{{ tftp_server }}"
+ file_name: "{{ irmc_filename }}"
+ irmc_flash_selector: "Auto"
+ irmc_boot_selector: "Auto"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# fw_update_configuration returned for command "get":
+ bios_file_name:
+ description: BIOS file name
+ returned: always
+ type: string
+ sample: D3279-B1x.R1.20.0.UPC
+ bios_version:
+ description: current BIOS version
+ returned: always
+ type: string
+ sample: V5.0.0.11 R1.20.0 for D3279-B1x
+ irmc_boot_selector:
+ description: selector for iRMC FW to boot
+ returned: always
+ type: string
+ sample: MostRecentProgrammedFW
+ irmc_file_name:
+ description: iRMC Firmware image name
+ returned: always
+ type: string
+ sample: D3279_09.09F_sdr03.12.bin
+ irmc_flash_selector:
+ description: selector for iRMC FW to flash
+ returned: always
+ type: string
+ sample: Auto
+ .BooterVersion:
+ description: booter version
+ returned: always
+ type: string
+ sample: 8.08
+ .FirmwareBuildDate:
+ description: firmware build date
+ returned: always
+ type: string
+ sample: "Dec 1 2017 21:36:17 CEST"
+ .FirmwareRunningState:
+ description: firmware running state
+ returned: always
+ type: string
+ sample: Inactive
+ .FirmwareVersion:
+ description: iRMC firmware version
+ returned: always
+ type: string
+ sample: 9.04F
+ .ImageDescription:
+ description: firmware image description
+ returned: always
+ type: string
+ sample: PRODUCTION RELEASE
+ .SDRRId:
+ description: sensor data record repository id
+ returned: always
+ type: string
+ sample: 0464
+ .SDRRVersion:
+ description: sensor data record repository version
+ returned: always
+ type: string
+ sample: 3.11
+ power_state:
+ description: server power state
+ returned: always
+ type: string
+ sample: Off
+ tftp_server_name:
+ description: TFTP server name
+ returned: always
+ type: string
+ sample: tftpserver.local
+
+# For "update" command:
+ Default return values:
+'''
+
+
+from builtins import str
+
+import json
+import time
+from datetime import datetime
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_post, irmc_redfish_patch, get_irmc_json
+from ansible.module_utils.irmc_upload_file import irmc_redfish_post_file
+
+
+# Global
+result = dict()
+
+
+def irmc_fwbios_update(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ preliminary_parameter_check(module)
+
+ # check that all tasks are finished properly
+ check_all_tasks_are_finished(module)
+
+ # Get iRMC basic data
+ status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ # Get iRMC FW Update data
+ update_url = "redfish/v1/Managers/iRMC/Oem/ts_fujitsu/iRMCConfiguration/FWUpdate/"
+ status, fwdata, msg = irmc_redfish_get(module, update_url)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=fwdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ if module.params['command'] == "get":
+ result['fw_update_configuration'] = setup_resultdata(fwdata, sysdata)
+ module.exit_json(**result)
+ elif module.params['update_type'] == "irmc":
+ patch_update_data(module, update_url, get_irmc_json(fwdata.json(), "@odata.etag"))
+
+ if module.params['update_source'] == "file":
+ status, udata, msg = irmc_redfish_post_file(module, get_update_url(module), module.params['file_name'])
+ else:
+ status, udata, msg = irmc_redfish_post(module, get_update_url(module), module.params['file_name'])
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=udata)
+ elif status not in (200, 202, 204):
+ if status == 104 and module.params['update_type'] == "irmc":
+ msg = "{0} This message might indicate that iRMC needs to reboot before FW update.".format(msg)
+ if status == 400:
+ msg = "{0} This message might be due to the binary file being invalid for the server.".format(msg)
+ module.fail_json(msg=msg, status=status)
+
+ wait_for_update_to_finish(module, udata.headers['Location'], get_irmc_json(sysdata.json(), "PowerState"))
+ module.exit_json(**result)
+
+
+def get_update_url(module):
+ if module.params['update_source'] == "tftp":
+ module.params['file_name'] = ""
+ if module.params['update_type'] == "irmc":
+ url = "redfish/v1/Managers/iRMC/Actions/FTSManager.FWTFTPUpdate"
+ else:
+ url = "redfish/v1/Systems/0/Bios/Actions/Oem/FTSBios.BiosTFTPUpdate"
+ else:
+ if module.params['update_type'] == "irmc":
+ url = "redfish/v1/Managers/iRMC/Actions/FTSManager.FWUpdate"
+ else:
+ url = "redfish/v1/Systems/0/Bios/Actions/Oem/FTSBios.BiosUpdate"
+ return url
+
+
+def preliminary_parameter_check(module):
+ if module.params['command'] == "update":
+ if module.params['update_source'] is None or module.params['update_type'] is None or \
+ module.params['file_name'] is None:
+ result['msg'] = "Command 'update' requires 'update_source, update_type, file_name' parameters to be set!"
+ result['status'] = 10
+ module.fail_json(**result)
+ if module.params['update_source'] == "tftp" and module.params['server_name'] is None:
+ result['msg'] = "TFTP update requires 'server_name' parameter to be set!"
+ result['status'] = 11
+ module.fail_json(**result)
+
+ if module.params['ignore_power_on'] is False:
+ # Get server power state
+ status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ if get_irmc_json(sysdata.json(), "PowerState") == "On":
+ result['skipped'] = True
+ result['warnings'] = "Server is powered on. Cannot continue."
+ module.exit_json(**result)
+
+
+# irmc: A BIOS firmware update has been started. A system reboot is required to continue with the update.
+# Status: FlashImageDownloadedSuccessfully (0% / 33%) [Running]
+# Status: (0% / 0%) [Pending]
+def wait_for_update_to_finish(module, location, power_state):
+ rebootDone = None
+ start_time = time.time()
+ while True:
+ time.sleep(5)
+ elapsed_time = time.time() - start_time
+ # make sure the module does not get stuck if anything goes wrong
+ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ if elapsed_time > 30 * 60:
+ msg = "Timeout of 30 minutes exceeded. Abort."
+ module.fail_json(msg=msg, status=20)
+
+ status, sdata, msg = irmc_redfish_get(module, "{0}".format(location[1:]))
+ if status == 99:
+ time.sleep(55)
+ continue
+ elif status == 404:
+ if rebootDone is True:
+ result['changed'] = True
+ break
+ continue
+ elif status == 503:
+ if rebootDone is None:
+ rebootDone = False # just in case we miss the 'complete' message
+ else:
+ rebootDone = True
+ time.sleep(25)
+ continue
+ elif status < 100 or (status not in (200, 202, 204)):
+ time.sleep(5)
+ continue
+
+ if "Key" in get_irmc_json(sdata.json(), "error"):
+ rebootDone = False
+ oemstate = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "StatusOEM"])
+ state_progress = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "StateProgressPercent"])
+ overall_progress = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "TotalProgressPercent"])
+ state = get_irmc_json(sdata.json(), "TaskState")
+ # make sure the process ran through
+ if power_state == "On" and oemstate == "Pending":
+ msg = "A BIOS firmware update has been started and a system reboot is required to continue the update."
+ result['warnings'] = msg
+ break
+ if power_state == "On" and oemstate == "FlashImageDownloadedSuccessfully":
+ msg = "A BIOS firmware update has been started. A system reboot is required to continue the update."
+ result['warnings'] = msg
+ break
+ if state == "Exception":
+ msg = "{0}: Update failed.".format(now)
+ module.fail_json(msg=msg, status=21)
+ # for BIOS we are done here, for iRMC we need to wait for iRMC shutdown and reboot
+ if module.params['update_type'] == "bios" and state == "Completed":
+ result['changed'] = True
+ break
+ else:
+ break
+
+
+def patch_update_data(module, update_url, etag):
+ body = {}
+ if module.params['update_source'] == "tftp":
+ body['ServerName'] = module.params['server_name']
+ if module.params['update_type'] == "irmc":
+ body['iRMCFileName'] = module.params['file_name']
+ else:
+ body['BiosFileName'] = module.params['file_name']
+ if module.params['irmc_flash_selector'] is not None:
+ body['iRMCFlashSelector'] = module.params['irmc_flash_selector']
+ if module.params['irmc_boot_selector'] is not None:
+ body['iRMCBootSelector'] = module.params['irmc_boot_selector']
+
+ if body != {}:
+ status, patch, msg = irmc_redfish_patch(module, update_url, json.dumps(body), etag)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=patch)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+
+def check_all_tasks_are_finished(module):
+ status, taskdata, msg = irmc_redfish_get(module, "redfish/v1/TaskService/Tasks")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=taskdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ tasks = get_irmc_json(taskdata.json(), ["Members"])
+ for task in tasks:
+ url = get_irmc_json(task, "@odata.id")
+ status, sdata, msg = irmc_redfish_get(module, "{0}".format(url[1:]))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ task_state = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "StatusOEM"])
+ if task_state in ("Pending", "FlashImageDownloadedSuccessfully"):
+ msg = "Firmware update has already been started, system reboot is required. Cannot continue new update."
+ module.fail_json(msg=msg, status=30)
+ task_progress = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "TotalProgressPercent"])
+ if str(task_progress) != "100":
+ msg = "Task '{0}' is still in progress. Cannot continue new update.". \
+ format(get_irmc_json(sdata.json(), "Name"))
+ module.fail_json(msg=msg, status=31)
+
+
+def setup_resultdata(data, sysdata):
+ configuration = {
+ 'tftp_server_name': get_irmc_json(data.json(), "ServerName"),
+ 'irmc_file_name': get_irmc_json(data.json(), "iRMCFileName"),
+ 'irmc_flash_selector': get_irmc_json(data.json(), "iRMCFlashSelector"),
+ 'irmc_boot_selector': get_irmc_json(data.json(), "iRMCBootSelector"),
+ 'irmc_fw_low': get_irmc_json(data.json(), "iRMCFwImageLow"),
+ 'irmc_fw_high': get_irmc_json(data.json(), "iRMCFwImageHigh"),
+ 'bios_file_name': get_irmc_json(data.json(), "BiosFileName"),
+ 'bios_version': get_irmc_json(sysdata.json(), "BiosVersion"),
+ 'power_state': get_irmc_json(sysdata.json(), "PowerState"),
+ }
+ return configuration
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="get", choices=['get', 'update']),
+ ignore_power_on=dict(required=False, type="bool", default=False),
+ update_source=dict(required=False, type="str", choices=['tftp', 'file']),
+ update_type=dict(required=False, type="str", choices=['irmc', 'bios']),
+ server_name=dict(required=False, type="str"),
+ file_name=dict(required=False, type="str"),
+ irmc_flash_selector=dict(required=False, type="str", choices=['Auto', 'LowFWImage', 'HighFWImage']),
+ irmc_boot_selector=dict(required=False, type="str", choices=['Auto', 'LowFWImage', 'HighFWImage']),
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_fwbios_update(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_getvm.py b/library/irmc_getvm.py
index d3a2540..013c7b1 100644
--- a/library/irmc_getvm.py
+++ b/library/irmc_getvm.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,34 +22,35 @@
description:
- Ansible module to get iRMC Virtual Media Data via iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
vm_type:
- description: the virtual media type whose data are to be read
+ description: The virtual media type whose data are to be read.
required: false
default: CDImage
choices: ['CDImage', 'FDImage', 'HDImage']
@@ -76,14 +77,65 @@
'''
RETURN = '''
-virtual_media_data:
- description: iRMC Virtual Media data
- returned: always
- type: dict
+# virtual_media_data returned by requesting data for e.g. 'CDImage':
+ CDImage:
+ description: state of image
+ returned: always
+ type: string
+ sample: Connected
+ bootmode:
+ description: boot source override mode for the next boot
+ returned: always
+ type: string
+ sample: UEFI
+ bootoverride:
+ description: boot override type
+ returned: always
+ type: string
+ sample: Once
+ bootsource:
+ description: boot device override for next boot
+ returned: always
+ type: string
+ sample: BiosSetup
+ image_name:
+ description: name of the virtual image
+ returned: always
+ type: string
+ sample: mybootimage.iso
+ server:
+ description: remote server where the image is located
+ returned: always
+ type: string
+ sample: 192.168.2.1
+ share_name:
+ description: path on the remote server where the image is located
+ returned: always
+ type: string
+ sample: isoimages
+ share_type:
+ description: share type (NFS or SMB)
+ returned: always
+ type: string
+ sample: NFS
+ usb_attach_mode:
+ description: remote image attach mode
+ returned: always
+ type: string
+ sample: AutoAttach
+ user_domain:
+ description: user domain for SMB share
+ returned: always
+ type: string
+ sample: local.net
+ user_name:
+ description: user name for SM share
+ returned: always
+ type: string
+ sample: test
'''
-# pylint: disable=wrong-import-position
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.irmc import irmc_redfish_get, get_irmc_json
@@ -102,7 +154,7 @@ def irmc_getvirtualmedia(module):
# get iRMC system data
status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
+ module.fail_json(msg=msg, status=status, exception=sysdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -127,21 +179,21 @@ def irmc_getvirtualmedia(module):
# eet iRMC Virtual Media data
status, vmdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/VirtualMedia/")
if status < 100:
- module.fail_json(msg=msg, exception=vmdata)
+ module.fail_json(msg=msg, status=status, exception=vmdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
# extract specified Virtual Media data
remotemountenabled = get_irmc_json(vmdata.json(), "RemoteMountEnabled")
if not remotemountenabled:
- vmdict['remote_mount_disabled'] = "WARNING: Remote Mount of Virtual Media is not enabled!"
+ vmdict['remote_mount_disabled'] = "Remote Mount of Virtual Media is not enabled!"
vmdict['usb_attach_mode'] = get_irmc_json(vmdata.json(), "UsbAttachMode")
vmdict['bootsource'] = get_irmc_json(sysdata.json(), ["Boot", "BootSourceOverrideTarget"])
vmdict['bootoverride'] = get_irmc_json(sysdata.json(), ["Boot", "BootSourceOverrideEnabled"])
vmdict['bootmode'] = get_irmc_json(sysdata.json(), ["Boot", "BootSourceOverrideMode"])
maxdevno = get_irmc_json(vmdata.json(), [module.params['vm_type'], "MaximumNumberOfDevices"])
if maxdevno == 0:
- vmdict['no_vm_configured'] = "WARNING: No Virtual Media of Type '" + module.params['vm_type'] + \
+ vmdict['no_vm_configured'] = "No Virtual Media of Type '" + module.params['vm_type'] + \
"' is configured!"
else:
vmdict['image_name'] = get_irmc_json(vmdata.json(), [module.params['vm_type'], "ImageName"])
diff --git a/library/irmc_idled.py b/library/irmc_idled.py
index 75775c5..a4f237d 100644
--- a/library/irmc_idled.py
+++ b/library/irmc_idled.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,39 +22,40 @@
description:
- Ansible module to get or set server ID LED via iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: get or set server ID LED state
+ description: Get or set server ID LED state.
required: false
default: get
choices: ['get', 'set']
state:
- description: desired server ID LED state for command 'set', ignored otherwise
+ description: Desired server ID LED state for command 'set', ignored otherwise.
required: false
choices: ['Off', 'Lit', 'Blinking']
@@ -91,14 +92,18 @@
'''
RETURN = '''
-idled_state:
- description: server ID LED state
- returned: always
- type: str
+# For command "get":
+ idled_state:
+ description: server ID LED state
+ returned: always
+ type: string
+ sample: Blinking
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
@@ -118,12 +123,13 @@ def irmc_idled(module):
# preliminary parameter check
if module.params['command'] == "set" and module.params['state'] is None:
result['msg'] = "Command 'set' requires 'state' parameter to be set!"
+ result['status'] = 10
module.fail_json(**result)
# get iRMC system data
status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
+ module.fail_json(msg=msg, status=status, exception=sysdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -142,6 +148,7 @@ def irmc_idled(module):
if module.params['state'] not in allowedparams:
result['msg'] = "Invalid parameter '{0}'. Allowed: {1}".format(module.params['state'],
json.dumps(allowedparams))
+ result['status'] = 11
module.fail_json(**result)
# set iRMC system data
@@ -149,7 +156,7 @@ def irmc_idled(module):
etag = get_irmc_json(sysdata.json(), "@odata.etag")
status, patch, msg = irmc_redfish_patch(module, "redfish/v1/Systems/0/", json.dumps(body), etag)
if status < 100:
- module.fail_json(msg=msg, exception=patch)
+ module.fail_json(msg=msg, status=status, exception=patch)
elif status != 200:
module.fail_json(msg=msg, status=status)
diff --git a/library/irmc_ldap.py b/library/irmc_ldap.py
index bb30e69..824f786 100644
--- a/library/irmc_ldap.py
+++ b/library/irmc_ldap.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,112 +22,113 @@
description:
- Ansible module to manage iRMC LDAP settings via iRMC remote scripting interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: get or set iRMC LDAP data
+ description: Get or set iRMC LDAP data.
required: false
default: get
choices: ['get', 'set']
enabled:
- description: LDAP enabled
+ description: LDAP enabled.
required: false
ssl_enabled:
- description: LDAP SSL enabled
+ description: LDAP SSL enabled.
required: false
local_login_disabled:
- description: local login disabled
+ description: Local login disabled.
required: false
always_use_ssl:
- description: always use SSL login
+ description: Always use SSL login.
required: false
directory_type:
- description: directory server type
+ description: Directory server type.
required: false
choices: ['MS Active Directory', 'Novell eDirector', 'Sun ePlanet', 'OpenLDAP', 'OpenDS / OpenDJ']
auth_type:
- description: authorization type
+ description: Authorization type.
required: false
- choices: ['Automatic', 'Settings stored on iRMC', 'Settings stored on LDAP']
+ choices: ['Automatic', 'Stored on iRMC', 'Stored on LDAP']
primary_server:
- description: primary LDAP server
+ description: Primary LDAP server.
required: false
primary_port:
- description: non-SL port of primary LDAP server
+ description: Non-SL port of primary LDAP server.
required: false
primary_ssl_port:
- description: SSL port of primary LDAP server
+ description: SSL port of primary LDAP server.
required: false
backup_server:
- description: backup LDAP server
+ description: Backup LDAP server.
required: false
backup_port:
- description: non-SL port of backup LDAP server
+ description: Non-SL port of backup LDAP server.
required: false
backup_ssl_port:
- description: SSL port of backup LDAP server
+ description: SSL port of backup LDAP server.
required: false
domain_name:
- description: domain name
+ description: Domain name.
required: false
department_name:
- description: department name
+ description: Department name.
required: false
base_dn:
- description: base DN
+ description: Base DN.
required: false
group_dn:
- description: groups directory as sub-tree from base DN
+ description: Groups directory as sub-tree from base DN.
required: false
user_search_context:
- description: user search context
+ description: User search context.
required: false
ldap_user:
- description: LDAP user name
+ description: LDAP user name.
required: false
ldap_password:
- description: LDAP user password
+ description: LDAP user password.
required: false
user_dn:
- description: principal user DN
+ description: Principal user DN.
required: false
append_base_to_user_dn:
- description: append base DN to principal user DN
+ description: Append base DN to principal user DN.
required: false
enhanced_user_login:
- description: enhanced user login
+ description: Enhanced user login.
required: false
user_login_filter:
- description: user login search filter
+ description: User login search filter.
required: false
alert_email_enabled:
- description: LDAP email alert enabled
+ description: LDAP email alert enabled.
required: false
alert_table_refresh:
- description: LDAP alert table refresh in hours (0 = never)
+ description: LDAP alert table refresh in hours (0 = never).
required: false
notes:
@@ -164,29 +165,147 @@
'''
RETURN = '''
-ldap:
- description: LDAP information
- returned: always
- type: dict
+# ldap data returned by command "get":
+ alert_email_enabled:
+ description: LDAP email alert enabled
+ returned: always
+ type: bool
+ sample: False
+ alert_table_refresh:
+ description: LDAP alert table refresh in hours
+ returned: always
+ type: string
+ sample: 0
+ always_use_ssl:
+ description: always use SSL login
+ returned: always
+ type: bool
+ sample: False
+ append_base_to_user_dn:
+ description: append base DN to principal user DN
+ returned: always
+ type: bool
+ sample: False
+ auth_type:
+ description: authorization type
+ returned: always
+ type: string
+ sample: Automatic
+ backup_port:
+ description: non-SL port of backup LDAP server
+ returned: always
+ type: string
+ sample: 389
+ backup_server:
+ description: backup LDAP server
+ returned: always
+ type: string
+ sample: ldap_backup.local
+ backup_ssl_port:
+ description: SSL port of backup LDAP server
+ returned: always
+ type: string
+ sample: 636
+ base_dn:
+ description: base DN
+ returned: always
+ type: string
+ sample:
+ department_name:
+ description: department name
+ returned: always
+ type: string
+ sample: department
+ directory_type:
+ description: directory server type
+ returned: always
+ type: string
+ sample: MS Active Directory
+ domain_name:
+ description: domain name
+ returned: always
+ type: string
+ sample: domain.local
+ enabled:
+ description: LDAP enabled
+ returned: always
+ type: bool
+ sample: True
+ enhanced_user_login:
+ description: enhanced user login
+ returned: always
+ type: string
+ sample: False
+ group_dn:
+ description: groups directory as sub-tree from base DN
+ returned: always
+ type: string
+ sample: ou=ldaptest
+ ldap_user:
+ description: LDAP user name
+ returned: always
+ type: string
+ sample: Administrator
+ local_login_disabled:
+ description: local login disabled
+ returned: always
+ type: bool
+ sample: False
+ primary_port:
+ description: non-SL port of primary LDAP server
+ returned: always
+ type: string
+ sample: 389
+ primary_server:
+ description: primary LDAP server
+ returned: always
+ type: string
+ sample: ldap_primary.local
+ primary_ssl_port:
+ description: SSL port of primary LDAP server
+ returned: always
+ type: string
+ sample: 636
+ ssl_enabled:
+ description: LDAP SSL enabled
+ returned: always
+ type: bool
+ sample: False
+ user_dn:
+ description: principal user DN
+ returned: always
+ type: string
+ sample:
+ user_login_filter:
+ description: user login search filter
+ returned: always
+ type: string
+ sample: (&(objectclass=person)(cn=%s))
+ user_search_context:
+ description: user search context
+ returned: always
+ type: string
+ sample:
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, setup_datadict, \
- get_key_for_value, add_scci_command, \
- scci_body_start, scci_body_end
+ setup_commandlist
ldap_dir = {"0": "MS Active Directory", "1": "Novell eDirectory", "2": "Sun ePlanet", "3": "OpenLDAP",
"4": "OpenDS / OpenDJ"}
-ldap_auth = {"0": "Automatic", "1": "Settings stored on iRMC", "2": "Settings stored on LDAP"}
+ldap_auth = {"0": "Automatic", "1": "Stored on iRMC", "2": "Stored on LDAP"}
true_false = {"0": "False", "1": "True"}
param_scci_map = [
# Param, SCCI Name, SCCI Code, index, value dict
["enabled", "ConfBMCLDAPEnable", 0x1971, 0, true_false], # iRMC: LDAP Enabled
- ["ssl_enabled", "ConfBMCLDAPSSLEnable", 0x1972, 0, true_false], # iRMC: LDPA SSL Enabled
+ ["ssl_enabled", "ConfBMCLDAPSSLEnable", 0x1972, 0, true_false], # iRMC: LDAP SSL Enabled
["local_login_disabled", "ConfBMCLDAPLocalLoginDisabled", 0x197B, 0, true_false], # iRMC: Disable Local Login
["always_use_ssl", "ConfBMCLDAPBrowserLoginDisabled", 0x197C, 0, true_false], # iRMC: always use SSL Login
["directory_type", "ConfBMCLDAPDirectoryType", 0x1974, 0, ldap_dir], # iRMC: Directory Server Type
@@ -208,8 +327,8 @@
["append_base_to_user_dn", "ConfBMCLdapAppendBaseDNtoUserDN", 0x197F, 0, true_false], # iRMC: Append Base DN
["enhanced_user_login", "ConfLDAPUseUserFilter", 0x1995, 0, true_false], # iRMC: Enhanced User Login
["user_login_filter", "ConfLdapUserFilter", 0x1994, 0, None], # iRMC: User Login Search filter
- ["alert_email_enabled", "ConfBMCLDAPAlertEnable", 0x1973, 0, true_false], # iRMC: Email Alert Enable
- ["alert_table_refresh", "ConfBMCLdapAlertRefreshTime", 0x1991, 0, None], # iRMC: Alert Table Refresh
+ ["alert_email_enabled", "ConfBMCLDAPAlertEnable", 0x1973, 0, true_false], # iRMC: Email Alert Enable
+ ["alert_table_refresh", "ConfBMCLdapAlertRefreshTime", 0x1991, 0, None], # iRMC: Alert Table Refresh
]
@@ -228,6 +347,7 @@ def irmc_ldap(module):
# preliminary parameter check
if module.params['command'] == "set" and setparam_count == 0:
result['msg'] = "Command 'set' requires at least one parameter to be set!"
+ result['status'] = 10
module.fail_json(**result)
if module.params['command'] == "set":
@@ -238,38 +358,24 @@ def irmc_ldap(module):
# send command list to scripting interface
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
# evaluate results list
ldapdata, scciresult, sccicontext = get_scciresultlist(data.content, ldapdata, param_scci_map)
if scciresult != 0:
- result['msg'] = sccicontext
- module.fail_json(**result)
+ module.fail_json(msg=sccicontext, status=scciresult)
if module.params['command'] == "get":
- result['ldap'] = setup_resultdict(ldapdata)
+ result['ldap'] = setup_resultdata(ldapdata)
else:
result['changed'] = True
module.exit_json(**result)
-def setup_commandlist(cmdlist, ctype, scci_map):
- body = scci_body_start
- data = ""
- for elem in scci_map:
- if elem[4] is not None and cmdlist[elem[0]] is not None:
- data = get_key_for_value(cmdlist[elem[0]], elem[4])
- else:
- data = cmdlist[elem[0]]
- body += add_scci_command(ctype, scci_map, elem[1], elem[3], data)
- body += scci_body_end
- return body
-
-
-def setup_resultdict(data):
+def setup_resultdata(data):
data = {
'enabled': true_false.get(data['enabled']),
'ssl_enabled': true_false.get(data['ssl_enabled']),
@@ -289,7 +395,6 @@ def setup_resultdict(data):
'group_dn': data['group_dn'],
'user_search_context': data['user_search_context'],
'ldap_user': data['ldap_user'],
- 'ldap_password': data['ldap_password'],
'user_dn': data['user_dn'],
'append_base_to_user_dn': true_false.get(data['append_base_to_user_dn']),
'enhanced_user_login': true_false.get(data['enhanced_user_login']),
@@ -316,7 +421,7 @@ def main():
choices=["MS Active Directory", "Novell eDirectory", "Sun ePlanet", "OpenLDAP",
"OpenDS / OpenDJ"]),
auth_type=dict(required=False, type="str",
- choices=["Automatic", "Settings stored on iRMC", "Settings stored on LDA"]),
+ choices=["Automatic", "Stored on iRMC", "Stored on LDAP"]),
primary_server=dict(required=False, type="str"),
primary_port=dict(required=False, type="int"),
primary_ssl_port=dict(required=False, type="int"),
diff --git a/library/irmc_license.py b/library/irmc_license.py
index f637220..025bf6c 100644
--- a/library/irmc_license.py
+++ b/library/irmc_license.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,38 +22,39 @@
description:
- Ansible module to manage iRMC user accounts via iRMC remote scripting interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: license key management to be executed
+ description: License key management to be executed.
required: false
default: get
choices: ['get', 'set']
license_key:
- description: iRMC license key to be set
+ description: iRMC license key to be set.
required: false
notes:
@@ -91,17 +92,20 @@
'''
RETURN = '''
-license_key:
- description: iRMC license key
- returned: always
- type: string
+# For command "get":
+ license_key:
+ description: system-locked iRMC license key
+ returned: always
+ type: string
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.irmc_scci_utils import get_scciresult,irmc_scci_post, add_scci_command, \
+from ansible.module_utils.irmc_scci_utils import get_scciresult, irmc_scci_post, add_scci_command, \
scci_body_start, scci_body_end
@@ -123,6 +127,7 @@ def irmc_license(module):
if module.params['command'] == "set" and module.params['license_key'] is None:
result['msg'] = "Command 'set' requires 'license_key' to be set!"
+ result['status'] = 10
module.fail_json(**result)
body = scci_body_start
@@ -134,14 +139,13 @@ def irmc_license(module):
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
licensekey, scciresult, sccicontext = get_scciresult(data.content, 0x1980)
if scciresult != 0:
- result['msg'] = sccicontext
- module.fail_json(**result)
+ module.fail_json(msg=sccicontext, status=scciresult)
if module.params['command'] == "get":
result['license_key'] = licensekey
diff --git a/library/irmc_ntp.py b/library/irmc_ntp.py
new file mode 100644
index 0000000..529c4af
--- /dev/null
+++ b/library/irmc_ntp.py
@@ -0,0 +1,240 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_ntp
+
+short_description: manage iRMC time options
+
+description:
+ - Ansible module to manage iRMC time options via iRMC remote scripting interface.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: NTP management to be executed.
+ required: false
+ default: get
+ choices: ['get', 'set']
+ time_mode:
+ description: Defines how iRMC synchronizes its real-time clock (RTC).
+ required: false
+ choices: ["System RTC", "NTP", "MMB NTP"]
+ rtc_mode:
+ description: Defines how iRMC interprets the system's hardware RTC time.
+ required: false
+ choices: ["local time", "UTC/GMT"]
+ time_zone_location:
+ description: iRMC time zone (e.g. 'Europe/Berlin'; based on Linux 'tzdata').
+ required: false
+ ntp_server_primary:
+ description: IP address (IPv4 or IPv6) or DNS name of primary NTP server.
+ required: false
+ ntp_server_secondary:
+ description: IP address (IPv4 or IPv6) or DNS name of secondary NTP server.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/12563/wp-svs-irmc-remote-scripting-en.pdf
+ - See https://sp.ts.fujitsu.com/dmsp/Publications/public/dp-svs-configuration-space-values-en.pdf
+'''
+
+EXAMPLES = '''
+# Get iRMC time settings
+- name: Get iRMC time settingsa
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ register: time
+ delegate_to: localhost
+- name: Show iRMC time settings
+ debug:
+ msg: "{{ time.time_settings }}"
+
+# Set iRMC time option(s)
+- name: Set iRMC time option(s)
+ irmc_ntp:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "set"
+ time_mode: "System RTC"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# time_settings returned for command "get":
+ ntp_server_primary:
+ description: primary NTP server
+ returned: always
+ type: string
+ sample: pool.ntp.org
+ ntp_server_secondary:
+ description: secondary NTP server
+ returned: always
+ type: string
+ sample: pool.ntp.org
+ rtc_mode:
+ description: Defines how iRMC interprets the system's hardware RTC time
+ returned: always
+ type: string
+ sample: local time
+ time_mode:
+ description: Defines how iRMC synchronizes its real-time clock (RTC)
+ returned: always
+ type: string
+ sample: System RTC
+ time_zone_location:
+ description: iRMC time zone
+ returned: always
+ type: string
+ sample: Europe/Berlin
+
+# For command "set":
+ Default return values:
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc_scci_utils import get_scciresultlist, irmc_scci_post, \
+ setup_datadict, setup_commandlist
+
+time_mode = {"0": "System RTC", "1": "NTP", "2": "MMB NTP"}
+rtc_mode = {"0": "local time", "1": "UTC/GMT"}
+true_false = {"0": "False", "1": "True"}
+param_scci_map = [
+ # Param, SCCI Name, SCCI Code, index, value dict
+ ["time_mode", "ConfBmcTimeSyncSource", 0x00B4, 0, time_mode], # iRMC: Time Mode
+ ["rtc_mode", "ConfBmcRtcMode", 0x00B6, 0, rtc_mode], # iRMC: RTC Mode
+ ["time_zone", "ConfBMCTimeZone", 0x00B2, 0, None], # iRMC: -
+ ["time_zone_location", "ConfBmcTimeZoneLocation", 0x00B5, 0, None], # iRMC: Time Zone Location
+ ["ntp_server_primary", "ConfBmcNtpServer", 0x00B3, 0, None], # iRMC: Primary NTP Server
+ ["ntp_server_secondary", "ConfBmcNtpServer", 0x00B3, 1, None], # iRMC: Secondary NTP Server
+ ["mmb_time_sync", "", 0x00B7, 0, true_false], # iRMC: -
+]
+
+
+def irmc_ntp(module):
+ result = dict(
+ changed=False,
+ status=0
+ )
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ module.params['time_zone'] = None
+ module.params['mmb_time_sync'] = None
+ ntpdata, setparam_count = setup_datadict(module)
+
+ # preliminary parameter checks
+ if module.params['command'] == "set":
+ if setparam_count < 0:
+ result['msg'] = "Command 'set' requires at least one parameter to be changed!"
+ result['status'] = 10
+ module.fail_json(**result)
+
+ # set up command list
+ if module.params['command'] == "get":
+ body = setup_commandlist(ntpdata, "GET", param_scci_map)
+ else:
+ body = setup_commandlist(ntpdata, "SET", param_scci_map)
+
+ # send command list to scripting interface
+ status, data, msg = irmc_scci_post(module, body)
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # evalaute result list
+ ntpdata, scciresult, sccicontext = get_scciresultlist(data.content, ntpdata, param_scci_map)
+ if scciresult != 0:
+ module.fail_json(msg=sccicontext, status=scciresult)
+
+ if module.params['command'] == "get":
+ result['time_settings'] = setup_resultdata(ntpdata)
+ else:
+ result['changed'] = True
+
+ module.exit_json(**result)
+
+
+def setup_resultdata(data):
+ result = {
+ 'time_mode': time_mode.get(data['time_mode']),
+ 'rtc_mode': rtc_mode.get(data['rtc_mode']),
+ 'time_zone_location': data['time_zone_location'],
+ 'ntp_server_primary': data['ntp_server_primary'],
+ 'ntp_server_secondary': data['ntp_server_secondary'],
+ }
+ return result
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="get", choices=["get", "set"]),
+ time_mode=dict(required=False, type="str", choices=["System RTC", "NTP", "MMB NTP"]),
+ rtc_mode=dict(required=False, type="str", choices=["local time", "UTC/GMT"]),
+ time_zone_location=dict(required=False, type="str"),
+ ntp_server_primary=dict(required=False, type="str"),
+ ntp_server_secondary=dict(required=False, type="str")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_ntp(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_powerstate.py b/library/irmc_powerstate.py
index 093a73c..eb058a1 100644
--- a/library/irmc_powerstate.py
+++ b/library/irmc_powerstate.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -23,41 +23,42 @@
description:
- Ansible module to get or set server power state via iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: get or set server power state
+ description: Get or set server power state.
required: false
default: get
choices: ['get', 'set']
state:
- description: desired server power state for command 'set', ignored otherwise;
- options 'GracefulPowerOff' and ' GracefulReset' require
- ServerView Agents running on server
+ description: Desired server power state for command 'set', ignored otherwise.
+ Options 'GracefulPowerOff' and ' GracefulReset' require
+ ServerView Agents running on server.
required: false
choices: ['PowerOn', 'PowerOff', 'PowerCycle', 'GracefulPowerOff', 'ImmediateReset', 'GracefulReset',
'PulseNmi', 'PressPowerButton']
@@ -95,14 +96,18 @@
'''
RETURN = '''
-power_state:
- description: server power state
- returned: always
- type: str
+# For command "get":
+ power_state:
+ description: server power state
+ returned: always
+ type: string
+ sample: "On"
+
+# For command "set":
+ Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
@@ -122,12 +127,13 @@ def irmc_powerstate(module):
# preliminary parameter check
if module.params['command'] == "set" and module.params['state'] is None:
result['msg'] = "Command 'set' requires 'state' parameter to be set!"
+ result['status'] = 10
module.fail_json(**result)
# Get iRMC system data
status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
+ module.fail_json(msg=msg, status=status, exception=sysdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -137,9 +143,9 @@ def irmc_powerstate(module):
module.exit_json(**result)
# Evaluate function params against iRMC
- if "Power" + power_state == module.params['state']:
+ if "Power" + power_state == module.params['state'].replace("Graceful", ""):
result['skipped'] = True
- result['msg'] = "PRIMERGY server is already in state '{0}'".format(module.params['state'])
+ result['msg'] = "PRIMERGY server is already in state '{0}'".format(power_state)
module.exit_json(**result)
allowedparams = \
@@ -150,6 +156,7 @@ def irmc_powerstate(module):
if module.params['state'] not in allowedparams:
result['msg'] = "Invalid parameter '{0}'. Allowed: {1}". \
format(module.params['state'], json.dumps(allowedparams))
+ result['status'] = 11
module.fail_json(**result)
# Set iRMC system data
@@ -157,8 +164,8 @@ def irmc_powerstate(module):
status, sysdata, msg = irmc_redfish_post(module, "redfish/v1/Systems/0/Actions/Oem/FTSComputerSystem.Reset",
json.dumps(body))
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
result['changed'] = True
diff --git a/library/irmc_profiles.py b/library/irmc_profiles.py
new file mode 100644
index 0000000..a8577c4
--- /dev/null
+++ b/library/irmc_profiles.py
@@ -0,0 +1,351 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_profiles
+
+short_description: handle iRMC profiles
+
+description:
+ - Ansible module to configure the BIOS boot oder via iRMC.
+ - Using this module may force server into several reboots.
+ - See specification [iRMC RESTful API](http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf).
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - The PRIMERGY server needs to be at least a M2 model.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: How to handle iRMC profiles.
+ required: false
+ default: list
+ choices: ['list', 'get', 'create', 'delete', 'import']
+ profile:
+ description: Which iRMC profile to handle.
+ Only relevant for 'get', 'create', 'delete'.
+ required: false
+ profile_json:
+ description: Direct input of iRMC profile data.
+ Only evaluated for command='import'. When set, 'profile_path' is ignored.
+ required: false
+ profile_path:
+ description: Path file where to read a profile.
+ Only evaluated for command='import'. Ignored when 'profile_json' is set.
+ required: false
+ wait_for_finish:
+ description: Wait for 'create profile' or 'import profile' session to finish. Ignored otherwise.
+ required: false
+ default: true
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# List iRMC profiles
+- name: List iRMC profiles
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get iRMC HWConfigurationIrmc profile
+- name: Get iRMC HWConfigurationIrmc profile
+ irmc_profiles:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ profile: "HWConfigurationIrmc"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# profiles returned for command "list":
+ :
+ description: name of specific profile
+ returned: always
+ type: dict
+ sample: BiosBootOrder
+ .Location:
+ description: RedFish location of profile
+ returned: always
+ type: string
+ sample: rest/v1/Oem/eLCM/ProfileManagement/BiosBootOrder
+ .Name:
+ description: name of profile
+ returned: always
+ type: string
+ sample: BiosBootOrder
+
+# profile data returned for command "get":
+ profile:
+ description: data of requested profile
+ returned: always
+ type: dict
+
+# For all other commands:
+ Default return values:
+'''
+
+
+import json
+import os.path
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_post, irmc_redfish_delete, get_irmc_json, \
+ waitForSessionToFinish
+
+
+# Global
+result = dict()
+
+
+def irmc_profiles(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ if module.params['command'] == "import":
+ if module.params['profile_json'] is None and not os.path.isfile(module.params['profile_path']):
+ module.fail_json(msg="Got no profile to import.", status=10)
+ if module.params['command'] not in ('list', 'import') and module.params['profile'] is None:
+ result['msg'] = "Command '{0}' requires parameter 'profile' to be set.".format(module.params['command'])
+ result['status'] = 11
+ module.fail_json(**result)
+
+ # start doing the actual work
+ if module.params['command'] == "list":
+ list_profiles(module)
+
+ if module.params['command'] == "get":
+ get_profile(module)
+
+ if module.params['command'] == "create":
+ create_profile(module)
+
+ if module.params['command'] == "delete":
+ delete_profile(module)
+
+ if module.params['command'] == "import":
+ import_profile(module)
+
+ module.exit_json(**result)
+
+
+def import_profile(module):
+ if module.params['profile_json'] is None:
+ try:
+ with open(module.params['profile_path']) as infile:
+ irmc_profile = json.load(infile)
+ except Exception:
+ result['msg'] = "Could not read JSON profile data from file '{0}'".format(module.params['profile_path'])
+ result['status'] = 20
+ module.fail_json(**result)
+ else:
+ try:
+ irmc_profile = json.loads(module.params['profile_json'])
+ except Exception:
+ result['msg'] = "Profile data are not proper JSON '{0}'.".format(module.params['profile_json'])
+ result['status'] = 21
+ module.fail_json(**result)
+
+ irmc_profile = checkandupdate_irmc_profile(module, irmc_profile)
+
+ # Set new profile
+ status, sysdata, msg = irmc_redfish_post(module, "rest/v1/Oem/eLCM/ProfileManagement/set",
+ json.dumps(irmc_profile))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status == 404:
+ result['msg'] = "Requested profile '{0}' cannot be imported.".format(module.params['profile'])
+ module.fail_json(msg=msg, status=status)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ if module.params['wait_for_finish'] is True:
+ # check that current session is terminated
+ status, data, msg = waitForSessionToFinish(module, get_irmc_json(sysdata.json(), ["Session", "Id"]))
+ if status > 30 and status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, log=data, status=status)
+
+ result['changed'] = True
+
+
+def delete_profile(module):
+ status, sysdata, msg = irmc_redfish_delete(module, "/rest/v1/Oem/eLCM/ProfileManagement/{0}".
+ format(module.params['profile']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status == 404:
+ result['msg'] = "Profile '{0}' does not exist.".format(module.params['profile'])
+ result['skipped'] = True
+ module.exit_json(**result)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ # 'delete' does not create a session, no need to wait
+ result['changed'] = True
+
+
+def create_profile(module):
+ url = "rest/v1/Oem/eLCM/ProfileManagement/get?PARAM_PATH=Server/{0}".format(module.params['profile'])
+ status, sysdata, msg = irmc_redfish_post(module, url, "")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status == 404:
+ result['msg'] = "Requested profile '{0}' cannot be created.".format(module.params['profile'])
+ module.fail_json(msg=msg, status=status)
+ elif status == 409:
+ result['msg'] = "Requested profile '{0}' already exists.".format(module.params['profile'])
+ result['skipped'] = True
+ module.exit_json(**result)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ if module.params['wait_for_finish'] is True:
+ # check that current session is terminated
+ status, data, msg = waitForSessionToFinish(module, get_irmc_json(sysdata.json(), ["Session", "Id"]))
+ if status > 30 and status < 100:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, log=data, status=status)
+
+ result['changed'] = True
+
+
+def list_profiles(module):
+ status, sysdata, msg = irmc_redfish_get(module, "/rest/v1/Oem/eLCM/ProfileManagement")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ result['profiles'] = {}
+ for profile in get_irmc_json(sysdata.json(), ["Links", "profileStore"]):
+ for key, value in profile.items():
+ profile = {}
+ profile['Name'] = value.replace("rest/v1/Oem/eLCM/ProfileManagement/", "")
+ profile['Location'] = value
+ result['profiles'][value.replace("rest/v1/Oem/eLCM/ProfileManagement/", "")] = profile
+
+
+def get_profile(module):
+ status, sysdata, msg = irmc_redfish_get(module, "/rest/v1/Oem/eLCM/ProfileManagement/{0}".
+ format(module.params['profile']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sysdata)
+ elif status == 404:
+ module.fail_json(msg="Requested profile '{0}' does not exist.".
+ format(module.params['profile']), status=status)
+ elif status != 200:
+ module.fail_json(msg=msg, status=status)
+
+ result['profile'] = sysdata.json()
+
+
+def checkandupdate_irmc_profile(module, profile):
+ server = get_irmc_json(profile, ['Server'])
+ if "Key does not exist" in server:
+ module.fail_json(msg="Invalid iRMC JSON: '{0}'.".format(profile), status=30)
+ else:
+ sysconfig = get_irmc_json(profile, ['Server', 'SystemConfig'])
+ if "Key does not exist" in sysconfig:
+ adapterconfig = get_irmc_json(profile, ['Server', 'AdapterConfigIrmc'])
+ if "Key does not exist" in adapterconfig:
+ hwconfig = get_irmc_json(profile, ['Server', 'HWConfigurationIrmc'])
+ if "Key does not exist" in hwconfig:
+ module.fail_json(msg="Invalid iRMC JSON: '{0}'.".format(profile), status=31)
+ else:
+ profile['Server']['HWConfigurationIrmc']['@Processing'] = "execute"
+ else:
+ profile['Server']['AdapterConfigIrmc']['@Processing'] = "execute"
+ else:
+ biosconfig = get_irmc_json(profile, ['Server', 'SystemConfig', 'BiosConfig'])
+ irmcconfig = get_irmc_json(profile, ['Server', 'SystemConfig', 'IrmcConfig'])
+ if "Key does not exist" in biosconfig and "Key does not exist" in irmcconfig:
+ msg = "Invalid iRMC JSON: '{0}'.".format(profile)
+ return 3, msg
+ if "Key does not exist" not in biosconfig:
+ biosbootorder = get_irmc_json(profile, ['Server', 'SystemConfig', 'BiosConfig', 'BiosBootOrder'])
+ if "Key does not exist" not in biosbootorder:
+ profile['Server']['SystemConfig']['BiosConfig']['BiosBootOrder']['BootOrderApply'] = True
+ profile['Server']['SystemConfig']['BiosConfig']['@Processing'] = "execute"
+ if "Key does not exist" not in irmcconfig:
+ profile['Server']['SystemConfig']['IrmcConfig']['@Processing'] = "execute"
+ return profile
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="list",
+ choices=['list', 'get', 'create', 'delete', 'import']),
+ profile=dict(required=False, type="str"),
+ profile_json=dict(required=False, type="str"),
+ profile_path=dict(required=False, type="str"),
+ wait_for_finish=dict(required=False, type="bool", default=True),
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_profiles(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_scci.py b/library/irmc_scci.py
index dd674b1..d2b76c2 100644
--- a/library/irmc_scci.py
+++ b/library/irmc_scci.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,61 +22,62 @@
description:
- Ansible module to execute iRMC Remote Scripting (SCCI) commands.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: SCCI remote scripting command (opcode)
+ description: SCCI remote scripting command.
required: true
choices:
- - get_cs # ConfigSpace Read
- - set_cs # ConfigSpace Write
- - power_on # Power-On the Server
- - power_off # Power-Off the Server
- - power_cycle # Power Cycle the Server
- - reset # Hard Reset the Server
- - nmi # Pulse the NMI (Non Maskable Interrupt)
- - graceful_shutdown # Graceful Shutdown, requires running Agent
- - graceful_reboot # Graceful Reboot, requires running Agent
- - cancel_shutdown # Cancel a Shutdown Request
- - reset_firmware # Perform a BMC Reset
- - connect_fd # Connect/Disconnect a Floppy image on a Remote Share (iRMC S4 only)
- - connect_cd # Connect/Disconnect a CD/DVD image on a Remote Share (iRMC S4 only)
- - connect_hd # Connect/Disconnect a HDD image on a Remote Share (iRMC S4 only)
+ - get_cs (ConfigSpace Read)
+ - set_cs (ConfigSpace Write)
+ - power_on (Power-On the Server)
+ - power_off (Power-Off the Server)
+ - power_cycle (Power Cycle the Server)
+ - reset (Hard Reset the Server)
+ - nmi (Pulse the NMI (Non Maskable Interrupt))
+ - graceful_shutdown (Graceful Shutdown, requires running Agent)
+ - graceful_reboot (Graceful Reboot, requires running Agent)
+ - cancel_shutdown (Cancel a Shutdown Request)
+ - reset_firmware (Perform a BMC Reset)
+ - connect_fd (Connect/Disconnect a Floppy image on a Remote Share (iRMC S4 only))
+ - connect_cd (Connect/Disconnect a CD/DVD image on a Remote Share (iRMC S4 only))
+ - connect_hd (Connect/Disconnect a HDD image on a Remote Share (iRMC S4 only))
opcodeext:
- description: SCCI opcode extension
+ description: SCCI opcode extension.
required: false
index:
- description: SCCI index
+ description: SCCI index.
required: false
cabid:
- description: SCCI cabinet ID
+ description: SCCI cabinet ID.
default: -1 (main cabinet)
required: false
data:
- description: data for commands which require data, ignored otherwise
+ description: Data for commands which require data, ignored otherwise.
required: false
notes:
@@ -113,14 +114,18 @@
'''
RETURN = '''
-data:
- description: SCCI command result
- returned: always
- type: str
+# For command "get_cs":
+ data:
+ description: result of requested SCCI command
+ returned: always
+ type: string
+ sample: In a galaxy far, far away ...
+
+# For all other commands:
+ Default return values:
'''
-# pylint: disable=wrong-import-position
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.irmc_scci_utils import setup_sccirequest, get_scciresult, irmc_scci_post
@@ -157,6 +162,7 @@ def irmc_scci(module):
if module.params['command'] == "set_cs" and module.params['data'] is None:
result['msg'] = "SCCI SET command requires 'data' parameter!"
+ result['status'] = 10
module.fail_json(**result)
body = setup_sccirequest(module, scci_code_map)
@@ -164,16 +170,16 @@ def irmc_scci(module):
# send command to scripting interface
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
# evaluate command result
- sccidata, result['sccistatus'], sccicontext = get_scciresult(data.content, module.params['opcodeext'])
- if result['sccistatus'] != 0:
+ sccidata, result['status'], sccicontext = get_scciresult(data.content, module.params['opcodeext'])
+ if result['status'] != 0:
result['msg'] = "SCCI '{0}' command was not successful. Return code {1}: {2}". \
- format(module.params['command'], result['sccistatus'], sccicontext)
- if result['sccistatus'] == 95:
+ format(module.params['command'], result['status'], sccicontext)
+ if result['status'] == 95:
result['exception'] = sccidata
module.fail_json(**result)
diff --git a/library/irmc_session.py b/library/irmc_session.py
new file mode 100644
index 0000000..b158a04
--- /dev/null
+++ b/library/irmc_session.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_session
+
+short_description: handle iRMC sessions
+
+description:
+ - Ansible module to handle iRMC sessions via Restful API.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: Handle iRMC sessions.
+ required: false
+ default: list
+ choices: ['list', 'get', 'remove', 'terminate', 'clearall']
+ id:
+ description: Specific session to get, remove or terminate.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# List iRMC sessions
+- name: List iRMC sessions
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get specific session information
+- name: Get specific session information
+ irmc_session:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# session_log.SessionLog data returned for command "get":
+ session_log.SessionLog.Entries.Entry:
+ description: list of individual session log entries
+ returned: always
+ type: list
+ session_log.SessionLog.Id:
+ description: Session ID
+ returned: always
+ type: int
+ sample: 4
+ session_log.SessionLog.Tag:
+ description: session tag
+ returned: always
+ type: string
+ sample:
+ session_log.SessionLog.WorkSequence:
+ description: work sequence
+ returned: always
+ type: string
+ sample: prepareOfflineUpdate
+ session_status:
+ description: session status
+ returned: always
+ type: string
+ sample: terminated regularly
+
+# data for sessions returned for command "list":
+ Duration:
+ description: Session duration in seconds
+ returned: always
+ type: int
+ sample: 226
+ Id:
+ description: session ID
+ returned: always
+ type: string
+ sample: 4
+ Start:
+ description: work sequence
+ returned: always
+ type: string
+ sample: 2018/07/31 12:09:25
+ Status:
+ description: session status
+ returned: always
+ type: string
+ sample: terminated regularly
+ Tag:
+ description: session tag
+ returned: always
+ type: string
+ sample:
+ Text:
+ description: work sequence
+ returned: always
+ type: string
+ sample: offlineUpdatePrepare
+
+# For all other commands:
+ Default return values:
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, irmc_redfish_delete, get_irmc_json
+
+
+# Global
+result = dict()
+
+
+def irmc_session(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ if (module.params['command'] in ("get", "remove", "terminate")) and module.params['id'] is None:
+ result['msg'] = "Command '{0}' requires 'id' parameter to be set!".format(module.params['command'])
+ result['status'] = 10
+ module.fail_json(**result)
+
+ sessions = get_irmc_sessions(module)
+ if module.params['command'] == "list":
+ result['sessions'] = {}
+ id_found = 0
+ for key, session in sessions.items():
+ for item in session:
+ if module.params['command'] == "clearall":
+ id_found += 1
+ if "Profile" not in item['#text']:
+ continue
+ handle_irmc_session(module, "remove", item)
+ elif module.params['command'] == "list":
+ id_found += 1
+ result['sessions']['session{0}'.format(item['@Id'])] = get_irmc_session_info(module, item)
+ else:
+ if item['@Id'] == module.params['id']:
+ id_found += 1
+ if module.params['command'] in ("terminate", "remove") and \
+ "Profile" not in item['#text'] and "Update" not in item['#text'] and \
+ "Configuration" not in item['#text']:
+ result['msg'] = "Session '{0}/{1}' is owned by iRMC. It cannot be {2}d.". \
+ format(item['@Id'], item['#text'], module.params['command'])
+ result['status'] = 11
+ module.fail_json(**result)
+ handle_irmc_session(module, module.params['command'], item)
+
+ if id_found == 0 and module.params['command'] != "list":
+ result['msg'] = "Session with ID '{0}' does not exist.".format(module.params['id'])
+ if module.params['command'] in ("get"):
+ result['status'] = 12
+ module.fail_json(**result)
+ else:
+ result['skipped'] = True
+
+ module.exit_json(**result)
+
+
+def get_irmc_sessions(module):
+ status, sessiondata, msg = irmc_redfish_get(module, "sessionInformation")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sessiondata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ return get_irmc_json(sessiondata.json(), ["SessionList"])
+
+
+def get_irmc_session_info(module, item):
+ status, sdata, msg = irmc_redfish_get(module, "sessionInformation/{0}/status".format(item['@Id']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ session = {}
+ session['Id'] = item['@Id']
+ session['Text'] = item['#text']
+ session['Tag'] = item['@Tag']
+ session['Status'] = get_irmc_json(sdata.json(), ["Session", "Status"])
+ session['Start'] = get_irmc_json(sdata.json(), ["Session", "Start"])
+ session['Duration'] = get_irmc_json(sdata.json(), ["Session", "Duration"])
+ return session
+
+
+def handle_irmc_session(module, command, item):
+ status, sdata, msg = irmc_redfish_get(module, "sessionInformation/{0}/status".format(item['@Id']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ sstatus = get_irmc_json(sdata.json(), ["Session", "Status"])
+ if command == "get":
+ status, sdata, msg = irmc_redfish_get(module, "sessionInformation/{0}/log".format(item['@Id']))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ result['session_status'] = sstatus
+ result['session_log'] = sdata.json()
+ module.exit_json(**result)
+ elif command == "terminate" and "terminat" in sstatus:
+ result['msg'] = "Session '{0}'/'{1}' is already terminated.".format(item['@Id'], item['#text'])
+ result['skipped'] = True
+ elif command == "remove" and "terminated" not in sstatus:
+ if module.params['command'] != "clearall":
+ result['msg'] = "Session '{0}'/'{1}' is not yet terminated and cannot be removed.". \
+ format(item['@Id'], item['#text'])
+ module.exit_json(**result)
+ else:
+ status, sdata, msg = irmc_redfish_delete(module, "sessionInformation/{0}/{1}".format(item['@Id'], command))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ result['changed'] = True
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="list",
+ choices=['list', 'get', 'remove', 'terminate', 'clearall']),
+ id=dict(required=False, type="int")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_session(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_setnextboot.py b/library/irmc_setnextboot.py
index e23c3cd..d35a419 100644
--- a/library/irmc_setnextboot.py
+++ b/library/irmc_setnextboot.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,44 +22,45 @@
description:
- Ansible module to configure iRMC to force next boot to specified option.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
bootsource:
- description: the source for the next boot
+ description: The source for the next boot.
required: false
default: BiosSetup
choices: ['None', 'Pxe', 'Floppy', 'Cd', 'Hdd', 'BiosSetup']
bootoverride:
- description: boot override type
+ description: Boot override type.
required: false
default: Once
choices: ['Once', 'Continuous']
bootmode:
- description: the mode for the next boot
+ description: The mode for the next boot.
required: false
choices: ['Legacy', 'UEFI']
@@ -84,14 +85,10 @@
'''
RETURN = '''
-result:
- description: nextboot action result
- returned: always
- type: dict
+Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
@@ -111,7 +108,7 @@ def irmc_setnextboot(module):
# Get iRMC system data
status, sysdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/")
if status < 100:
- module.fail_json(msg=msg, exception=sysdata)
+ module.fail_json(msg=msg, status=status, exception=sysdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -120,6 +117,7 @@ def irmc_setnextboot(module):
if module.params['bootsource'] not in bootsourceallowed:
result['msg'] = "Invalid parameter '" + module.params['bootsource'] + "' for function. Allowed: " + \
json.dumps(bootsourceallowed)
+ result['status'] = 10
module.fail_json(**result)
# evaluate parameters
@@ -127,6 +125,7 @@ def irmc_setnextboot(module):
if module.params['bootoverride'] not in bootoverrideallowed:
result['msg'] = "Invalid parameter '" + module.params['bootoverride'] + "' for function. Allowed: " + \
json.dumps(bootoverrideallowed)
+ result['status'] = 11
module.fail_json(**result)
# Set iRMC system data
@@ -141,7 +140,7 @@ def irmc_setnextboot(module):
etag = get_irmc_json(sysdata.json(), "@odata.etag")
status, patch, msg = irmc_redfish_patch(module, "redfish/v1/Systems/0/", json.dumps(body), etag)
if status < 100:
- module.fail_json(msg=msg, exception=patch)
+ module.fail_json(msg=msg, status=status, exception=patch)
elif status != 200:
module.fail_json(msg=msg, status=status)
diff --git a/library/irmc_setvm.py b/library/irmc_setvm.py
index cfd186f..be993f0 100644
--- a/library/irmc_setvm.py
+++ b/library/irmc_setvm.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,64 +22,65 @@
description:
- Ansible module to set iRMC Virtual Media Data via iRMC RedFish interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
vm_type:
- description: the virtual media type to be set
+ description: The virtual media type to be set.
required: false
default: CDImage
choices: ['CDImage', 'FDImage', 'HDImage']
server:
- description: remote server (IP or DNS name) where the image is located
+ description: Remote server (IP or DNS name) where the image is located.
required: true
share:
- description: path on the remote server where the image is located
+ description: Path on the remote server where the image is located.
required: true
image:
- description: name of the remote image
+ description: Name of the remote image.
required: true
share_type:
- description: share type (NFS share or SMB share)
+ description: Share type (NFS share or SMB share).
required: false
choices: ['NFS', 'SMB']
vm_domain:
- description: user domain in case of SMB share
+ description: User domain in case of SMB share.
required: false
vm_user:
- description: user account in case of SMB share
+ description: User account in case of SMB share.
required: false
vm_password:
- description: user password in case of SMB share
+ description: User password in case of SMB share.
required: false
force_remotemount_enabled:
- description: forces iRMC to enable the remote mount feature
+ description: Forces iRMC to enable the remote mount feature.
required: false
force_mediatype_active:
- description: forces iRMC to activate one of the required remote media types
+ description: Forces iRMC to activate one of the required remote media types.
required: false
notes:
@@ -103,14 +104,10 @@
'''
RETURN = '''
-result:
- description: virtual media set action result
- returned: always
- type: dict
+Default return values:
'''
-# pylint: disable=wrong-import-position
import json
from ansible.module_utils.basic import AnsibleModule
@@ -128,12 +125,12 @@ def irmc_setvirtualmedia(module):
result['msg'] = "module was not run"
module.exit_json(**result)
- vmparams, count = setup_datadict(module) # pylint: disable=unused-variable
+ vmparams, count = setup_datadict(module)
# Get iRMC Virtual Media data
status, vmdata, msg = irmc_redfish_get(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/VirtualMedia/")
if status < 100:
- module.fail_json(msg=msg, exception=vmdata)
+ module.fail_json(msg=msg, status=status, exception=vmdata)
elif status != 200:
module.fail_json(msg=msg, status=status)
@@ -142,6 +139,7 @@ def irmc_setvirtualmedia(module):
if maxdevno == 0:
if not module.params['force_mediatype_active']:
result['warnings'] = "No Virtual Media of Type '" + module.params['vm_type'] + "' is configured!"
+ result['status'] = 20
module.fail_json(**result)
else:
new_maxdevno = 1
@@ -150,7 +148,8 @@ def irmc_setvirtualmedia(module):
remotemountenabled = get_irmc_json(vmdata.json(), "RemoteMountEnabled")
if not remotemountenabled and not module.params['force_remotemount_enabled']:
- result['warnings'] = "Remote Mount of Virtual Media is not enabled!"
+ result['msg'] = "Remote Mount of Virtual Media is not enabled!"
+ result['status'] = 30
module.fail_json(**result)
# Set iRMC system data
@@ -159,7 +158,7 @@ def irmc_setvirtualmedia(module):
status, patch, msg = irmc_redfish_patch(module, "redfish/v1/Systems/0/Oem/ts_fujitsu/VirtualMedia/",
json.dumps(body), etag)
if status < 100:
- module.fail_json(msg=msg, exception=patch)
+ module.fail_json(msg=msg, status=status, exception=patch)
elif status != 200:
module.fail_json(msg=msg, status=status)
diff --git a/library/irmc_task.py b/library/irmc_task.py
new file mode 100644
index 0000000..b6ea704
--- /dev/null
+++ b/library/irmc_task.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'
+}
+
+
+DOCUMENTATION = '''
+---
+module: irmc_task
+
+short_description: handle iRMC tasks
+
+description:
+ - Ansible module to handle iRMC tasks via Restful API.
+ - Module Version V1.1.
+
+requirements:
+ - The module needs to run locally.
+ - iRMC S4 needs FW >= 9.04, iRMC S5 needs FW >= 1.25.
+ - Python >= 2.6
+ - Python module 'future'
+
+version_added: "2.4"
+
+author:
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
+
+options:
+ irmc_url:
+ description: IP address of the iRMC to be requested for data.
+ required: true
+ irmc_username:
+ description: iRMC user for basic authentication.
+ required: true
+ irmc_password:
+ description: Password for iRMC user for basic authentication.
+ required: true
+ validate_certs:
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
+ required: false
+ default: true
+ command:
+ description: Handle iRMC tasks.
+ required: false
+ default: list
+ choices: ['list', 'get']
+ id:
+ description: Specific task to get.
+ required: false
+
+notes:
+ - See http://manuals.ts.fujitsu.com/file/13371/irmc-restful-spec-en.pdf
+ - See http://manuals.ts.fujitsu.com/file/13372/irmc-redfish-wp-en.pdf
+'''
+
+EXAMPLES = '''
+# List iRMC tasks
+- name: List iRMC tasks
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "list"
+ delegate_to: localhost
+
+# Get specific task information
+- name: Get specific task information
+ irmc_task:
+ irmc_url: "{{ inventory_hostname }}"
+ irmc_username: "{{ irmc_user }}"
+ irmc_password: "{{ irmc_password }}"
+ validate_certs: "{{ validate_certificate }}"
+ command: "get"
+ id: 3
+ delegate_to: localhost
+'''
+
+RETURN = '''
+# task data returned for command "get":
+ Id:
+ description: task ID
+ returned: always
+ type: int
+ sample: 3
+ Name:
+ description: task name
+ returned: always
+ type: string
+ sample: ProfileParametersApply
+ StartTime:
+ description: start time
+ returned: always
+ type: string
+ sample: "2018-07-31 12:23:02"
+ EndTime:
+ description: end time
+ returned: always
+ type: string
+ sample: "2018-07-31 12:26:44"
+ State:
+ description: task state
+ returned: always
+ type: string
+ sample: Completed
+ StateOem:
+ description: Oem task state
+ returned: always
+ type: string
+ sample: LcmSessFinished
+ StateProgressPercent:
+ description: state progress in %
+ returned: always
+ type: string
+ sample: 100
+ TotalProgressPercent:
+ description: overall progress in %
+ returned: always
+ type: string
+ sample: 100
+
+# tasks data returned for command "list":
+ List of individual task entries (see above):
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible.module_utils.irmc import irmc_redfish_get, get_irmc_json
+
+
+# Global
+result = dict()
+
+
+def irmc_task(module):
+ # initialize result
+ result['changed'] = False
+ result['status'] = 0
+
+ if module.check_mode:
+ result['msg'] = "module was not run"
+ module.exit_json(**result)
+
+ # preliminary parameter check
+ if (module.params['command'] in ("get", "remove", "terminate")) and module.params['id'] is None:
+ result['msg'] = "Command '{0}' requires 'id' parameter to be set!".format(module.params['command'])
+ result['status'] = 10
+ module.fail_json(**result)
+
+ id_found = 0
+ if module.params['command'] == "get":
+ id_found = 1
+ result['task'] = get_irmc_task_info(module, "/redfish/v1/TaskService/Tasks/{0}".
+ format(module.params['id']), module.params['id'])
+
+ if module.params['command'] == "list":
+ status, taskdata, msg = irmc_redfish_get(module, "redfish/v1/TaskService/Tasks")
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=taskdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+ tasks = get_irmc_json(taskdata.json(), ["Members"])
+
+ result['tasks'] = []
+ for task in tasks:
+ id_found += 1
+ task_url = get_irmc_json(task, "@odata.id")
+ myID = task_url.replace("/redfish/v1/TaskService/Tasks/", "")
+ task_info = get_irmc_task_info(module, task_url, myID)
+ result['tasks'].append(task_info)
+
+ module.exit_json(**result)
+
+
+def get_irmc_task_info(module, url, task_id):
+ status, sdata, msg = irmc_redfish_get(module, "{0}".format(url[1:]))
+ if status < 100:
+ module.fail_json(msg=msg, status=status, exception=sdata)
+ elif status not in (200, 202, 204):
+ module.fail_json(msg=msg, status=status)
+
+ task = {}
+ task['Id'] = task_id
+ task['Name'] = get_irmc_json(sdata.json(), "Name")
+ task['State'] = get_irmc_json(sdata.json(), "TaskState")
+ task['StateOem'] = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "StatusOEM"])
+ task['StateProgressPercent'] = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "StateProgressPercent"])
+ task['TotalProgressPercent'] = get_irmc_json(sdata.json(), ["Oem", "ts_fujitsu", "TotalProgressPercent"])
+ task['StartTime'] = get_irmc_json(sdata.json(), "StartTime")
+ task['EndTime'] = get_irmc_json(sdata.json(), "EndTime")
+ return task
+
+
+def main():
+ # import pdb; pdb.set_trace()
+ module_args = dict(
+ irmc_url=dict(required=True, type="str"),
+ irmc_username=dict(required=True, type="str"),
+ irmc_password=dict(required=True, type="str", no_log=True),
+ validate_certs=dict(required=False, type="bool", default=True),
+ command=dict(required=False, type="str", default="list", choices=['list', 'get']),
+ id=dict(required=False, type="int")
+ )
+ module = AnsibleModule(
+ argument_spec=module_args,
+ supports_check_mode=False
+ )
+
+ irmc_task(module)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/library/irmc_user.py b/library/irmc_user.py
index 7a7f1e0..27c0cac 100644
--- a/library/irmc_user.py
+++ b/library/irmc_user.py
@@ -1,8 +1,8 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
@@ -22,167 +22,168 @@
description:
- Ansible module to manage iRMC user accounts via iRMC remote scripting interface.
- - Module Version V1.0.1.
+ - Module Version V1.1.
requirements:
- The module needs to run locally.
- - "python >= 2.6"
+ - Python >= 2.6
+ - Python module 'future'
version_added: "2.4"
author:
- - FujitsuPrimergy (@FujitsuPrimergy)
+ - Fujitsu Server PRIMERGY (@FujitsuPrimergy)
options:
irmc_url:
- description: IP address of the iRMC to be requested for data
+ description: IP address of the iRMC to be requested for data.
required: true
irmc_username:
- description: iRMC user for basic authentication
+ description: iRMC user for basic authentication.
required: true
irmc_password:
- description: password for iRMC user for basic authentication
+ description: Password for iRMC user for basic authentication.
required: true
validate_certs:
- description: evaluate SSL certificate (set to false for self-signed certificate)
+ description: Evaluate SSL certificate (set to false for self-signed certificate).
required: false
default: true
command:
- description: user management to be executed
+ description: User management to be executed.
required: false
default: get
choices: ['get', 'create', 'change', 'delete']
name:
- description: user account name
+ description: User account name.
required: true
password:
- description: user account password
+ description: User account password.
required: false
description:
- description: user account desciption
+ description: User account desciption.
required: false
enabled:
- description: user account enabled
+ description: User account enabled.
required: false
lan_privilege:
- description: IPMI LAN channel privilege
+ description: IPMI LAN channel privilege.
required: false
choices: ['Reserved', 'Callback', 'User', 'Operator', 'Administrator', 'OEM', 'NoAccess']
serial_privilege:
- description: IPMI serial channel privilege
+ description: IPMI serial channel privilege.
required: false
choices: ['Reserved', 'Callback', 'User', 'Operator', 'Administrator', 'OEM', 'NoAccess']
config_user_enabled:
- description: user may configure user accounts
+ description: User may configure user accounts.
required: false
config_bmc_enabled:
- description: user may configure iRMC settings
+ description: User may configure iRMC settings.
required: false
avr_enabled:
- description: user may use Advanved Video Redirection (AVR)
+ description: User may use Advanved Video Redirection (AVR)
required: false
storage_enabled:
- description: user may use Remote Storage
+ description: User may use Remote Storage.
required: false
redfish_enabled:
- description: user may use iRMC Redfish interface
+ description: User may use iRMC Redfish interface.
required: false
redfish_role:
- description: user account Redfish role
+ description: User account Redfish role.
required: false
choices: ['NoAccess', 'Operator', 'Administrator', 'ReadOnly']
shell:
- description: user text access type
+ description: User text access type.
required: false
choices: ['SMASH CLP', 'CLI', 'Remote Manager', 'IPMI basic mode', 'IPMI terminal mode', 'None']
snmpv3_enabled:
- description: user may use SNMPv3
+ description: User may use SNMPv3.
required: false
snmpv3_access:
- description: user account SNMPV3 access privilege
+ description: User account SNMPV3 access privilege.
required: false
choices: ['ReadOnly', 'ReadWrite', 'Other']
snmpv3_auth:
- description: user account SNMPv3 authentication
+ description: User account SNMPv3 authentication.
required: false
choices: ['Undefined', 'SHA', 'MD5', 'None']
snmpv3_privacy:
- description: user account SNMPv3 privacy type
+ description: User account SNMPv3 privacy type.
required: false
choices: ['Undefined', 'AES', 'DES', 'None']
ssh_public_key:
- description: user account SSH public key
+ description: user account SSH public key.
required: false
ssh_certificate:
- description: user account SSH certificate
+ description: User account SSH certificate.
required: false
email_enabled:
- description: alert email enabled
+ description: Alert email enabled.
required: false
email_encrypted:
- description: alert email is encrypted
+ description: Alert email is encrypted.
required: false
email_type:
- description: alert email format
+ description: Alert email format.
required: false
choices: ['Standard', 'ITS-Format', 'REMCS', 'Fixed Subject', 'SMS']
email_server:
- description: preferred mail server for alert email
+ description: Preferred mail server for alert email.
required: false
choices: ['Automatic', 'Primary', 'Secondary']
email_address:
- description: alert email address
+ description: Alert email address.
required: false
alert_fans:
- description: define alert level for fan sensors
+ description: Define alert level for fan sensors.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_temperatures:
- description: define alert level for temperature sensors
+ description: Define alert level for temperature sensors.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_hwerrors:
- description: define alert level for critical hardware errors
+ description: Define alert level for critical hardware errors.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_syshang:
- description: define alert level for system hang
+ description: Define alert level for system hang.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_posterrors:
- description: define alert level for POST errors
+ description: Define alert level for POST errors.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_security:
- description: define alert level for security
+ description: Define alert level for security.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_sysstatus:
- description: define alert level for system status
+ description: Define alert level for system status.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_hderrors:
- description: define alert level for disk drivers & controllers
+ description: Define alert level for disk drivers & controllers.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_network:
- description: define alert level for network interface
+ description: Define alert level for network interface.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_remote:
- description: define alert level for remote management
+ description: Define alert level for remote management.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_power:
- description: define alert level for system power
+ description: Define alert level for system power.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_memory:
- description: define alert level for memory
+ description: Define alert level for memory.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
alert_others:
- description: define alert level for other
+ description: Define alert level for other.
required: false
choices: ['None', 'Critical', 'Warning', 'All']
@@ -244,14 +245,197 @@
'''
RETURN = '''
-user:
- description: user account information
- returned: always
- type: dict
+# user data returned for command "get":
+ alert_fans:
+ description: alert level for fan sensors
+ returned: always
+ type: string
+ sample: Warning
+ alert_hderrors:
+ description: alert level for disk drivers & controllers
+ returned: always
+ type: string
+ sample: Critical
+ alert_hwerrors:
+ description: alert level for critical hardware errors
+ returned: always
+ type: string
+ sample: All
+ alert_memory:
+ description: alert level for memory
+ returned: always
+ type: string
+ sample: Critical
+ alert_network:
+ description: alert level for network interface
+ returned: always
+ type: string
+ sample: Warning
+ alert_others:
+ description: alert level for other
+ returned: always
+ type: string
+ sample: None
+ alert_posterrors:
+ description: alert level for POST errors
+ returned: always
+ type: string
+ sample: All
+ alert_power:
+ description: alert level for system power
+ returned: always
+ type: string
+ sample: Warning
+ alert_remote:
+ description: alert level for remote management
+ returned: always
+ type: string
+ sample: Critical
+ alert_security:
+ description: alert level for security
+ returned: always
+ type: string
+ sample: Warning
+ alert_syshang:
+ description: alert level for system hang
+ returned: always
+ type: string
+ sample: Critical
+ alert_sysstatus:
+ description: alert level for system status
+ returned: always
+ type: string
+ sample: None
+ alert_temperatures:
+ description: alert level for temperature sensors
+ returned: always
+ type: string
+ sample: Warning
+ avr_enabled:
+ description: user may use Advanved Video Redirection (AVR)
+ returned: always
+ type: bool
+ sample: True
+ config_bmc_enabled:
+ description: user may configure iRMC settings
+ returned: always
+ type: bool
+ sample: True
+ config_user_enabled:
+ description: user may configure user accounts
+ returned: always
+ type: bool
+ sample: True
+ description:
+ description: user account desciption
+ returned: always
+ type: string
+ sample: Admin User
+ email_address:
+ description: alert email address
+ returned: always
+ type: string
+ sample: admin@irmc.local
+ email_enabled:
+ description: alert email enabled
+ returned: always
+ type: bool
+ sample: False
+ email_encrypted:
+ description: alert email is encrypted
+ returned: always
+ type: bool
+ sample: False
+ email_server:
+ description: preferred mail server for alert email
+ returned: always
+ type: string
+ sample: Automatic
+ email_type:
+ description: alert email format
+ returned: always
+ type: string
+ sample: Standard
+ enabled:
+ description: user account enabled
+ returned: always
+ type: bool
+ sample: True
+ id:
+ description: user ID
+ returned: always
+ type: int
+ sample: 0
+ lan_privilege:
+ description: IPMI LAN channel privilege
+ returned: always
+ type: string
+ sample: Administrator
+ name:
+ description: user account name
+ returned: always
+ type: string
+ sample: admin
+ redfish_enabled:
+ description: user may use iRMC Redfish interface
+ returned: always
+ type: bool
+ sample: True
+ redfish_role:
+ description: user account Redfish role
+ returned: always
+ type: string
+ sample: Administrator
+ serial_privilege:
+ description: IPMI serial channel privilege
+ returned: always
+ type: string
+ sample: Administrator
+ shell:
+ description: user text access type
+ returned: always
+ type: string
+ sample: Remote Manager
+ snmpv3_access:
+ description: user account SNMPV3 access privilege
+ returned: always
+ type: string
+ sample: ReadOnly
+ snmpv3_auth:
+ description: user account SNMPv3 authentication
+ returned: always
+ type: string
+ sample: SHA
+ snmpv3_enabled:
+ description: user may use SNMPv3
+ returned: always
+ type: bool
+ sample: False
+ snmpv3_privacy:
+ description: user account SNMPv3 privacy type
+ returned: always
+ type: string
+ sample: DES
+ ssh_certificate:
+ description: user account SSH certificate
+ returned: always
+ type: string
+ ssh_public_key:
+ description: user account SSH public key
+ returned: always
+ type: string
+ storage_enabled:
+ description: user may use Remote Storage
+ returned: always
+ type: bool
+ sample: True
+
+# For all other commands:
+ Default return values:
+
'''
-# pylint: disable=wrong-import-position
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.irmc_scci_utils import get_scciresult, get_scciresultlist, irmc_scci_post, \
@@ -314,7 +498,7 @@
]
-def irmc_user(module): # pylint: disable=too-many-branches,too-many-statements
+def irmc_user(module):
result = dict(
changed=False,
status=0
@@ -330,13 +514,16 @@ def irmc_user(module): # pylint: disable=too-many-branches,too-many-state
if module.params['command'] == "change":
if setparam_count <= 1:
result['msg'] = "Command 'change' requires at least one parameter to be changed!"
+ result['status'] = 10
module.fail_json(**result)
if module.params['command'] == "create":
if module.params['password'] is None:
result['msg'] = "Command 'create' requires at least 'password' parameter to be set!"
+ result['status'] = 11
module.fail_json(**result)
if module.params['description'] is not None and len(module.params['description']) > 32:
result['msg'] = "Description can only be 32 characters long!"
+ result['status'] = 12
module.fail_json(**result)
# determine user ID (free or otherwise)
@@ -347,8 +534,8 @@ def irmc_user(module): # pylint: disable=too-many-branches,too-many-state
scci_body_end
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
username, sccistatus, sccicontext = get_scciresult(data.content, 0x1451)
@@ -379,16 +566,17 @@ def irmc_user(module): # pylint: disable=too-many-branches,too-many-state
module.exit_json(**result)
else:
result['msg'] = "Requested user '{0}' could not be found.".format(module.params['name'])
+ result['status'] = 20
module.fail_json(**result)
# set up command list
if module.params['command'] == "get":
- body = setup_commandlist(userdata, "GET", param_scci_map, userdata['id'])
+ body = setup_user_commandlist(userdata, "GET", param_scci_map, userdata['id'])
elif module.params['command'] == "change":
- body = setup_commandlist(userdata, "SET", param_scci_map, userdata['id'])
+ body = setup_user_commandlist(userdata, "SET", param_scci_map, userdata['id'])
elif module.params['command'] == "create":
userdata = set_default(userdata)
- body = setup_commandlist(userdata, "CREATE", param_scci_map, userdata['id'])
+ body = setup_user_commandlist(userdata, "CREATE", param_scci_map, userdata['id'])
elif module.params['command'] == "delete":
body = scci_body_start
body += add_scci_command("DELETE", param_scci_map, "ConfBMCAcctUserName", userdata['id'], "")
@@ -398,15 +586,14 @@ def irmc_user(module): # pylint: disable=too-many-branches,too-many-state
# send command list to scripting interface
status, data, msg = irmc_scci_post(module, body)
if status < 100:
- module.fail_json(msg=msg, exception=data)
- elif status != 200 and status != 204:
+ module.fail_json(msg=msg, status=status, exception=data)
+ elif status not in (200, 202, 204):
module.fail_json(msg=msg, status=status)
- # evalaute result list
+ # evaluate result list
userdata, scciresult, sccicontext = get_scciresultlist(data.content, userdata, param_scci_map)
if scciresult != 0:
- result['msg'] = sccicontext
- module.fail_json(**result)
+ module.fail_json(msg=sccicontext, status=scciresult)
userdata['name'] = module.params['name']
if module.params['command'] == "get":
@@ -417,7 +604,7 @@ def irmc_user(module): # pylint: disable=too-many-branches,too-many-state
module.exit_json(**result)
-def setup_commandlist(cmdlist, ctype, scci_map, user_id):
+def setup_user_commandlist(cmdlist, ctype, scci_map, user_id):
body = scci_body_start
data = ""
for elem in scci_map:
diff --git a/module_utils/irmc.py b/module_utils/irmc.py
index 2d8541f..cbf4b54 100644
--- a/module_utils/irmc.py
+++ b/module_utils/irmc.py
@@ -1,21 +1,23 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
+from builtins import str
+import time
import traceback
import json
import requests
from requests.auth import HTTPBasicAuth
-from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
-
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
-requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+import urllib3
+from urllib3.util.retry import Retry
+from urllib3.exceptions import InsecureRequestWarning
+urllib3.disable_warnings(InsecureRequestWarning)
def irmc_redfish_get(module, uri):
@@ -40,10 +42,10 @@ def irmc_redfish_get(module, uri):
if status != 200:
try:
msg = "GET request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
- except Exception: # pylint: disable=broad-except
+ except Exception:
msg = "GET request was not successful ({0}).".format(url)
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
status = 99
data = traceback.format_exc()
msg = "GET request encountered exception ({0}): {1}".format(url, str(e))
@@ -58,12 +60,13 @@ def irmc_redfish_patch(module, uri, body, etag):
data = msg
return 97, data, msg
- try:
- json.loads(body)
- except ValueError as e:
- data = traceback.format_exc()
- msg = "PATCH request got invalid JSON body: {0}".format(body)
- return 98, data, msg
+ if body != "":
+ try:
+ json.loads(body)
+ except ValueError as e:
+ data = traceback.format_exc()
+ msg = "PATCH request got invalid JSON body: {0}".format(body)
+ return 98, data, msg
headers = {
"Accept": "application/json",
@@ -87,10 +90,10 @@ def irmc_redfish_patch(module, uri, body, etag):
if status != 200:
try:
msg = "PATCH request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
- except Exception: # pylint: disable=broad-except
+ except Exception:
msg = "PATCH request was not successful ({0}).".format(url)
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
status = 99
data = traceback.format_exc()
msg = "PATCH request encountered exception ({0}): {1}".format(url, str(e))
@@ -125,13 +128,13 @@ def irmc_redfish_post(module, uri, body):
data.connection.close()
status = data.status_code
- if status != 200 and status != 202 and status != 204:
+ if status not in (200, 202, 204):
try:
msg = "POST request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
- except Exception: # pylint: disable=broad-except
+ except Exception:
msg = "POST request was not successful ({0}).".format(url)
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
status = 99
data = traceback.format_exc()
msg = "POST request encountered exception ({0}): {1}".format(url, str(e))
@@ -161,10 +164,10 @@ def irmc_redfish_delete(module, uri):
if status != 200:
try:
msg = "DELETE request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
- except Exception: # pylint: disable=broad-except
+ except Exception:
msg = "DELETE request was not successful ({0}).".format(url)
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
status = 99
data = traceback.format_exc()
msg = "DELETE request encountered exception ({0}): {1}".format(url, str(e))
@@ -195,7 +198,28 @@ def get_irmc_json(jsondata, keys):
data = jsondata[keys[0]][keys[1]][keys[2]][keys[3]][keys[4]][keys[5]]
else:
data = "Key too long ({0} levels): '{1}'".format(keylen, jsonkey)
- except Exception: # pylint: disable=broad-except
+ except Exception:
data = "Key does not exist: '{0}'".format(jsonkey)
return data
+
+
+def waitForSessionToFinish(module, sessionId):
+ while True:
+ status, sdata, msg = irmc_redfish_get(module, "sessionInformation/{0}/status".format(sessionId))
+ if status < 100 or (status not in (200, 202, 204)):
+ return status, sdata, msg
+
+ sstatus = get_irmc_json(sdata.json(), ["Session", "Status"])
+ if "terminated" not in sstatus:
+ time.sleep(10)
+ else:
+ msg = "Session result: {0}".format(sstatus)
+ if "error" in sstatus:
+ status, sdata, mmsg = irmc_redfish_get(module, "sessionInformation/{0}/log".format(sessionId))
+ if status < 100 or (status not in (200, 202, 204)):
+ return status, sdata, mmsg
+ sdata = sdata.json()
+ status = 29
+ break
+ return status, sdata, msg
diff --git a/module_utils/irmc_scci_utils.py b/module_utils/irmc_scci_utils.py
index d08d87c..7db2653 100644
--- a/module_utils/irmc_scci_utils.py
+++ b/module_utils/irmc_scci_utils.py
@@ -1,18 +1,19 @@
#!/usr/bin/python
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
+from builtins import str
import traceback
from xml.etree import cElementTree as ElementTree
import requests
from requests.auth import HTTPBasicAuth
-from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
scci_body_start = """\n"""
@@ -38,10 +39,25 @@ def setup_sccirequest(module, scci_map):
return body
+def setup_commandlist(cmdlist, ctype, scci_map):
+ body = scci_body_start
+ data = ""
+ for elem in scci_map:
+ if elem[0] not in cmdlist:
+ continue
+ if elem[4] is not None and cmdlist[elem[0]] is not None:
+ data = get_key_for_value(cmdlist[elem[0]], elem[4])
+ else:
+ data = cmdlist[elem[0]]
+ body += add_scci_command(ctype, scci_map, elem[1], elem[3], data)
+ body += scci_body_end
+ return body
+
+
def add_scci_command(ctype, scci_map, opcodeextcode, index, data):
- if ctype not in ["SET", "GET", "CREATE", "DELETE"]:
+ if ctype not in ("SET", "GET", "CREATE", "DELETE"):
return ""
- if ctype == "CREATE" or ctype == "DELETE":
+ if ctype in ("CREATE", "DELETE"):
ctype = "SET"
if ctype == "SET" and data is None:
return ""
@@ -72,7 +88,6 @@ def add_scci_command(ctype, scci_map, opcodeextcode, index, data):
return body
-# pylint: disable=too-many-branches
def get_scciresult(data, opcodeextcode):
# extract XML data
# things are complicated, as the return string is not necessarily well-formed.
@@ -109,13 +124,13 @@ def get_scciresult(data, opcodeextcode):
# sccidata = "Error: no results from command list."
# sccicontext = sccidata
pass
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
scciresult = 95
sccidata = "SCCI result was not correct XML: {0}".format(str(e))
sccicontext = traceback.format_exc()
# User SSH key is empty
- if scciresult == 1 and (opcodeextcode == 0x19A1 or opcodeextcode == 0x19A2 or opcodeextcode == 0x19A3):
+ if scciresult == 1 and opcodeextcode in (0x19A1, 0x19A2, 0x19A3):
scciresult = 0
sccidata = sccicontext = ""
@@ -154,7 +169,7 @@ def irmc_scci_post(module, body):
''' Post a SCCI command at iRMC config URI. '''
try:
ElementTree.fromstring(body)
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
data = traceback.format_exc()
msg = "POST request got invalid XML body: {0}".format(body)
return 98, data, msg
@@ -172,15 +187,45 @@ def irmc_scci_post(module, body):
data.connection.close()
status = data.status_code
- if status != 200 and status != 204:
+ if status not in (200, 202, 204):
+ try:
+ msg = "POST request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
+ except Exception:
+ msg = "POST request was not successful ({0}).".format(url)
+
+ if "Login required to continue." in str(data.content):
+ return 1, "Invalid login data.", "Login required to continue."
+ except Exception as e:
+ status = 99
+ data = traceback.format_exc()
+ msg = "POST request encountered exception ({0}): {1}".format(url, str(e))
+
+ return status, data, msg
+
+
+def irmc_scci_update(module, update_url):
+ session = requests.Session()
+ retries = Retry(total=5, backoff_factor=0.1)
+ session.mount('http://', HTTPAdapter(max_retries=retries))
+ session.mount('https://', HTTPAdapter(max_retries=retries))
+
+ url = "http://{0}/{1}".format(module.params['irmc_url'], update_url)
+ msg = "OK"
+ try:
+ data = session.post(url, verify=module.params['validate_certs'],
+ auth=HTTPBasicAuth(module.params['irmc_username'], module.params['irmc_password']))
+ data.connection.close()
+
+ status = data.status_code
+ if status not in (200, 202, 204):
try:
msg = "POST request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
- except Exception: # pylint: disable=broad-except
+ except Exception:
msg = "POST request was not successful ({0}).".format(url)
if "Login required to continue." in data.content:
return 1, "Invalid login data.", "Login required to continue."
- except Exception as e: # pylint: disable=broad-except
+ except Exception as e:
status = 99
data = traceback.format_exc()
msg = "POST request encountered exception ({0}): {1}".format(url, str(e))
@@ -193,7 +238,7 @@ def get_key_for_value(value, dictionary):
return ""
if dictionary is None or not isinstance(dictionary, dict):
return ""
- for dictkey, dictvalue in dictionary.iteritems():
+ for dictkey, dictvalue in dictionary.items():
value = str(value)
if value.lower() == dictvalue.lower():
return dictkey
@@ -202,19 +247,21 @@ def get_key_for_value(value, dictionary):
def get_sccicode(param_or_name, scci_map):
for elem in scci_map:
- if elem[0] == param_or_name or elem[1] == param_or_name:
+ if param_or_name in (elem[0], elem[1]):
return elem[2]
return 0
-def setup_datadict(module):
+def setup_datadict(module, emptyAllowed=True):
spcount = 0
datadict = dict()
- for key, value in module.params.iteritems():
- if key != "irmc_url" and key != "irmc_username" and key != "irmc_password" and \
- key != "validate_certs" and key != "command":
+ for key, value in module.params.items():
+ if key not in ("irmc_url", "irmc_username", "irmc_password", "validate_certs", "command"):
if value is not None:
- spcount += 1
+ if emptyAllowed is True or value != "":
+ spcount += 1
+ else:
+ value = None
datadict[key] = value
return datadict, spcount
diff --git a/module_utils/irmc_upload_file.py b/module_utils/irmc_upload_file.py
new file mode 100644
index 0000000..90b2962
--- /dev/null
+++ b/module_utils/irmc_upload_file.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+from builtins import str
+
+import ntpath
+import traceback
+import requests
+from requests.auth import HTTPBasicAuth
+from requests.adapters import HTTPAdapter
+import urllib3
+from urllib3.util.retry import Retry
+from urllib3.exceptions import InsecureRequestWarning
+from requests_toolbelt import MultipartEncoder
+urllib3.disable_warnings(InsecureRequestWarning)
+
+
+def irmc_redfish_post_file(module, uri, filename):
+ try:
+ filedata = open(filename, 'rb')
+ except Exception as e:
+ status = 89
+ fdata = traceback.format_exc()
+ msg = "Could not read file at '{0}': {1}".format(filename, str(e))
+ return status, fdata, msg
+
+ filebasename = ntpath.basename(filename)
+ multipart_data = MultipartEncoder(
+ fields={'data': (filebasename, filedata, 'application/octet-stream', {'Content-Disposition': 'form-data'})}
+ )
+ headers = {
+ "Accept": "application/json",
+ "Content-Type": multipart_data.content_type
+ }
+ url = "https://{0}/{1}".format(module.params['irmc_url'], uri)
+
+ session = requests.Session()
+ retries = Retry(total=5, backoff_factor=0.1)
+ session.mount('http://', HTTPAdapter(max_retries=retries))
+ session.mount('https://', HTTPAdapter(max_retries=retries))
+
+ msg = "OK"
+ try:
+ data = session.post(url, headers=headers, data=multipart_data, verify=module.params['validate_certs'],
+ auth=HTTPBasicAuth(module.params['irmc_username'], module.params['irmc_password']))
+ data.connection.close()
+
+ status = data.status_code
+ if status not in (200, 202, 204):
+ try:
+ msg = "POST request was not successful ({0}): {1}".format(url, data.json()['error']['message'])
+ except Exception:
+ msg = "POST request was not successful ({0}).".format(url)
+
+ except Exception as e:
+ status = 99
+ data = traceback.format_exc()
+ msg = "POST request encountered exception ({0}): {1}".format(url, str(e))
+
+ return status, data, msg
diff --git a/module_utils/irmc_utils.py b/module_utils/irmc_utils.py
new file mode 100644
index 0000000..384155e
--- /dev/null
+++ b/module_utils/irmc_utils.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+from builtins import range
+
+
+def compare_irmc_profile(profile1, profile2, key="", mykey="", complist=None):
+ result = True
+ if key != "":
+ if mykey != "":
+ mykey = "{0}.{1}".format(mykey, key)
+ else:
+ mykey = key
+ if complist is None:
+ complist = []
+
+ if type(profile1) != type(profile2):
+ complist.append("'{0}': type '{1}' != type '{2}'".format(mykey, type(profile1), type(profile2)))
+ result = False
+ elif is_final_type(profile1) or is_final_type(profile2):
+ if profile1 != profile2:
+ complist.append("'{0}': '{1}' != '{2}'".format(mykey, profile1, profile2))
+ result = False
+ elif isinstance(profile1, list) and isinstance(profile2, list):
+ new_result, complist = compare_irmc_profile_list(profile1, profile2, mykey, complist)
+ result &= new_result
+ else:
+ new_result, complist = compare_irmc_profile_dict(profile1, profile2, mykey, complist)
+ result &= new_result
+ return result, complist
+
+
+def compare_irmc_profile_dict(dict1, dict2, mykey="", complist=None):
+ result = True
+ if complist is None:
+ complist = []
+
+ if type(dict1) != type(dict2):
+ complist.append("'{0}': type '{1}' != type '{2}'".format(mykey, type(dict1), type(dict2)))
+ return False, complist
+
+ if len(dict1) != len(dict2):
+ complist.append("'{0}': dict len '{1}' != dict len '{2}'".format(mykey, len(dict1), len(dict2)))
+ result = False
+
+ odiff = set(dict1.keys()) - set(dict2.keys())
+ ndiff = set(dict2.keys()) - set(dict1.keys())
+ if odiff != ndiff:
+ complist.append("'{0}': missing keys '{1}', found keys '{2}'".format(mykey, " ".join(str(x) for x in odiff),
+ " ".join(str(x) for x in ndiff)))
+ result = False
+ for dko, dvo in sorted(dict1.items()):
+ for dkn, dvn in sorted(dict2.items()):
+ if dko == dkn:
+ new_result, complist = compare_irmc_profile(dvo, dvn, dko, mykey, complist)
+ result &= new_result
+ return result, complist
+
+
+def compare_irmc_profile_list(list1, list2, mykey="", complist=None):
+ result = True
+ if complist is None:
+ complist = []
+
+ if type(list1) != type(list2):
+ complist.append("'{0}': type '{1}' != type '{2}'".format(mykey, type(list1), type(list2)))
+ return False, complist
+
+ longlist = list1
+ shortlist = list2
+ if len(list1) != len(list2):
+ longside = "original"
+ complist.append("'{0}': list len '{1}' != list len '{2}'".format(mykey, len(list1), len(list2)))
+ if len(list1) < len(list2):
+ longside = "compared"
+ longlist = list2
+ shortlist = list1
+ result = False
+ # longlist.sort()
+ # shortlist.sort()
+ for index in range(0, len(shortlist)):
+ new_result, complist = compare_irmc_profile(shortlist[index], longlist[index], "",
+ "{0}[{1}]".format(mykey, index), complist)
+ result &= new_result
+ for index in range(len(shortlist) - 1, len(longlist) - 1):
+ complist.append("'{0}[{1}]': only on '{2}' side".format(mykey, index, longside))
+ result = False
+ return result, complist
+
+
+def is_final_type(this_var):
+ try: # check whether python knows about 'basestring'
+ basestring
+ except NameError: # no, so it is python3, use 'str' instead
+ basestring = str
+
+ retval = False
+ try: # check whether python knows about 'unicode'
+ if isinstance(this_var, unicode):
+ retval = True
+ except NameError: # no, so it is python3
+ retval = False
+ if isinstance(this_var, (basestring, bool, int)) or this_var is None:
+ retval = True
+ return retval
diff --git a/tests/test_irmc.py b/tests/test_irmc.py
index 10ba803..20e567d 100644
--- a/tests/test_irmc.py
+++ b/tests/test_irmc.py
@@ -1,19 +1,17 @@
#!/usr/bin/python
-# see https://www.relaxdiego.com/2016/09/writing-ansible-modules-002.html
-
-
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
+from builtins import str
import json
-import mock
import requests
from requests.exceptions import Timeout
+import mock
from ansible.compat.tests import unittest
from ansible.compat.tests.mock import create_autospec, patch
@@ -22,8 +20,6 @@
from module_utils import irmc
-# pylint: disable=too-many-public-methods
-# pylint: disable=unused-argument
class TestIrmc(unittest.TestCase):
# preparing the tests
@@ -95,7 +91,7 @@ def test__irmc_redfish_get__exception(self, get):
self.assertIn("GET request encountered exception (" + self.url + ")", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__all_is_well(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__all_is_well(self, patch):
requests.Session.patch.return_value = self.mockdata
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}), 12345)
self.assertEqual(self.mockdata.status_code, status)
@@ -103,7 +99,7 @@ def test__irmc_redfish_patch__all_is_well(self, patch): # pylint: disable=red
self.assertEqual("OK", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__etag_is_good_string(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__etag_is_good_string(self, patch):
requests.Session.patch.return_value = self.mockdata
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}),
"12345")
@@ -112,7 +108,7 @@ def test__irmc_redfish_patch__etag_is_good_string(self, patch): # pylint: dis
self.assertEqual("OK", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__etag_is_extra_large(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__etag_is_extra_large(self, patch):
requests.Session.patch.return_value = self.mockdata
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}),
1234567890123456789)
@@ -121,7 +117,7 @@ def test__irmc_redfish_patch__etag_is_extra_large(self, patch): # pylint: dis
self.assertEqual("OK", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__bad_etag(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__bad_etag(self, patch):
requests.Session.patch.return_value = self.mockdata
etag = "abcde"
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}), etag)
@@ -130,7 +126,7 @@ def test__irmc_redfish_patch__bad_etag(self, patch): # pylint: disable=redefi
self.assertEqual("etag is no number: " + etag, msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__bad_status_without_reason(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__bad_status_without_reason(self, patch):
requests.Session.patch.return_value = self.mockdata
self.mockdata.status_code = 100
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}), 12345)
@@ -139,7 +135,7 @@ def test__irmc_redfish_patch__bad_status_without_reason(self, patch): # pylin
self.assertEqual("PATCH request was not successful (" + self.url + ").", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__bad_status_with_reason(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__bad_status_with_reason(self, patch):
requests.Session.patch.return_value = self.mockdata
self.mockdata.json.return_value = self.mockdata.bad_return
self.mockdata.status_code = 100
@@ -149,7 +145,7 @@ def test__irmc_redfish_patch__bad_status_with_reason(self, patch): # pylint:
self.assertEqual("PATCH request was not successful (" + self.url + "): " + self.mockdata.reason, msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__bad_body(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__bad_body(self, patch):
requests.Session.patch.return_value = self.mockdata
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", "{ 'Patch': 'mockpatch' }", 12345)
self.assertEqual(98, status)
@@ -157,7 +153,7 @@ def test__irmc_redfish_patch__bad_body(self, patch): # pylint: disable=redefi
self.assertIn("PATCH request got invalid JSON body", msg)
@patch.object(requests.Session, 'patch')
- def test__irmc_redfish_patch__exception(self, patch): # pylint: disable=redefined-outer-name
+ def test__irmc_redfish_patch__exception(self, patch):
requests.Session.patch.side_effect = Timeout()
status, data, msg = irmc.irmc_redfish_patch(self.mod, "redfish_path", json.dumps({'Patch': 'mockpatch'}), 12345)
self.assertEqual(99, status)
@@ -232,6 +228,26 @@ def test__get_irmc_json__key_does_not_exist(self):
result = irmc.get_irmc_json(self.mockjson, key)
self.assertEqual("Key does not exist: '" + key + "'", result)
+ @patch.object(requests.Session, 'get')
+ def test__waitForSessionToFinish__session_terminated(self, get):
+ requests.Session.get.return_value = self.mockdata
+ self.mockdata.json.return_value = {'Session': {'Status': 'terminated'}}
+ status, data, msg = irmc.waitForSessionToFinish(self.mod, 1)
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertEqual(self.mockdata.json.return_value, data.json.return_value)
+ self.assertEqual("Session result: terminated", msg)
+
+ @patch.object(requests.Session, 'get')
+ def test__waitForSessionToFinish__session_error(self, get):
+ requests.Session.get.return_value = self.mockdata
+ self.mockdata.json.return_value = {'Session': {'Status': 'terminated with error'}}
+ status, data, msg = irmc.waitForSessionToFinish(self.mod, 1)
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertEqual(self.mockdata.json.return_value, data)
+ self.assertEqual("Session result: terminated with error", msg)
+
+ # Cannot test actually waiting for a session to finish ...
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_irmc_scci_utils.py b/tests/test_irmc_scci_utils.py
index 276d6ec..1b2f4ff 100644
--- a/tests/test_irmc_scci_utils.py
+++ b/tests/test_irmc_scci_utils.py
@@ -1,19 +1,17 @@
#!/usr/bin/python
-# see https://www.relaxdiego.com/2016/09/writing-ansible-modules-002.html
-
-
-# FUJITSU Limited
+# FUJITSU LIMITED
# Copyright 2018 FUJITSU LIMITED
-# GNU General Public License v3.0+ (see LICENSE.md or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division)
__metaclass__ = type
+from builtins import str
-import mock
import requests
from requests.exceptions import Timeout
from lxml import objectify, etree
+import mock
from ansible.compat.tests import unittest
from ansible.compat.tests.mock import create_autospec, patch
@@ -22,9 +20,7 @@
from module_utils import irmc_scci_utils
-# pylint: disable=too-many-public-methods
-# pylint: disable=unused-argument
-class TestIrmcScci(unittest.TestCase):
+class TestIrmcScciUtils(unittest.TestCase):
# preparing the tests
def setUp(self):
@@ -119,10 +115,12 @@ def test__setup_sccirequest__all_is_well_get(self):
"""""".format("E001", format(self.mod.params['opcodeext'], 'x'),
format(self.mod.params['index'], 'x'), self.mod.params['cabid'])
expectedxml += irmc_scci_utils.scci_body_end
+ expectedxml = expectedxml.replace(irmc_scci_utils.scci_body_start, '\n')
body = irmc_scci_utils.setup_sccirequest(self.mod, self.scci_code_map)
+ body = body.replace(irmc_scci_utils.scci_body_start, '\n')
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__setup_sccirequest__all_is_well_set_string(self):
self.mod.params['command'] = "set_cs"
@@ -135,10 +133,12 @@ def test__setup_sccirequest__all_is_well_set_string(self):
format(self.mod.params['index'], 'x'), self.mod.params['cabid'],
self.mod.params['data'])
expectedxml += irmc_scci_utils.scci_body_end
+ expectedxml = expectedxml.replace(irmc_scci_utils.scci_body_start, '\n')
body = irmc_scci_utils.setup_sccirequest(self.mod, self.scci_code_map)
+ body = body.replace(irmc_scci_utils.scci_body_start, '\n')
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__setup_sccirequest__all_is_well_set_int(self):
self.mod.params['command'] = "set_cs"
@@ -151,10 +151,12 @@ def test__setup_sccirequest__all_is_well_set_int(self):
format(self.mod.params['index'], 'x'), self.mod.params['cabid'],
self.mod.params['data'])
expectedxml += irmc_scci_utils.scci_body_end
+ expectedxml = expectedxml.replace(irmc_scci_utils.scci_body_start, '\n')
body = irmc_scci_utils.setup_sccirequest(self.mod, self.scci_code_map)
+ body = body.replace(irmc_scci_utils.scci_body_start, '\n')
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__get_scciresult__all_is_well_data_string_1455(self):
datastr = "TestData"
@@ -166,8 +168,8 @@ def test__get_scciresult__all_is_well_data_string_1455(self):
sccireturndata += irmc_scci_utils.scci_body_end
sccidata, scciresult, sccicontext = irmc_scci_utils.get_scciresult(sccireturndata, 0x1455)
self.assertEqual(status, scciresult)
- self.assertEquals("", sccicontext)
- self.assertEquals(datastr, sccidata)
+ self.assertEqual("", sccicontext)
+ self.assertEqual(datastr, sccidata)
def test__get_scciresult__all_is_well_data_integer_1457(self):
dataint = "999" # param data is always a string
@@ -179,8 +181,8 @@ def test__get_scciresult__all_is_well_data_integer_1457(self):
sccireturndata += irmc_scci_utils.scci_body_end
sccidata, scciresult, sccicontext = irmc_scci_utils.get_scciresult(sccireturndata, 0x1457)
self.assertEqual(status, scciresult)
- self.assertEquals("", sccicontext)
- self.assertEquals(dataint, sccidata)
+ self.assertEqual("", sccicontext)
+ self.assertEqual(dataint, sccidata)
def test__get_scciresult__all_is_well_overall(self):
status = 0
@@ -192,8 +194,8 @@ def test__get_scciresult__all_is_well_overall(self):
"""""".format(status)
sccidata, scciresult, sccicontext = irmc_scci_utils.get_scciresult(sccireturndata, 0)
self.assertEqual(status, scciresult)
- self.assertEquals("", sccicontext)
- self.assertEquals("", sccidata)
+ self.assertEqual("", sccicontext)
+ self.assertEqual("", sccidata)
def test__get_scciresult__bad_xml(self):
sccireturndata = """"""
@@ -215,8 +217,8 @@ def test__get_scciresult__bad_scci_status(self):
"""""".format(status, format(opcode, 'X'), datastr)
sccidata, scciresult, sccicontext = irmc_scci_utils.get_scciresult(sccireturndata, opcode)
self.assertEqual(status, scciresult)
- self.assertEquals("OpCodeExt 0x{0}: {1} ({2})".format(format(opcode, 'X'), datastr, status), sccicontext)
- self.assertEquals(datastr, sccidata)
+ self.assertEqual("OpCodeExt 0x{0}: {1} ({2})".format(format(opcode, 'X'), datastr, status), sccicontext)
+ self.assertEqual(datastr, sccidata)
def test__get_scciresult__bad_overall_status(self):
datastr = ""
@@ -230,8 +232,8 @@ def test__get_scciresult__bad_overall_status(self):
"""""".format(status)
sccidata, scciresult, sccicontext = irmc_scci_utils.get_scciresult(sccireturndata, opcode)
self.assertEqual(status, scciresult)
- self.assertEquals("Error {0}".format(status), sccicontext)
- self.assertEquals(datastr, sccidata)
+ self.assertEqual("Error {0}".format(status), sccicontext)
+ self.assertEqual(datastr, sccidata)
def test__get_scciresultlist__all_is_well_results_1455_1457(self):
datastr = "TestData"
@@ -249,9 +251,9 @@ def test__get_scciresultlist__all_is_well_results_1455_1457(self):
sccidata, scciresult, sccicontext = \
irmc_scci_utils.get_scciresultlist(sccireturndata, self.userdata, self.param_scci_map)
self.assertEqual(status, scciresult)
- self.assertEquals("", sccicontext)
- self.assertEquals(datastr, sccidata['description'])
- self.assertEquals(dataint, sccidata['enabled'])
+ self.assertEqual("", sccicontext)
+ self.assertEqual(datastr, sccidata['description'])
+ self.assertEqual(dataint, sccidata['enabled'])
def test__get_scciresultlist__bad_scci_status_1455_1457(self):
datastr = "String setting failed."
@@ -272,8 +274,8 @@ def test__get_scciresultlist__bad_scci_status_1455_1457(self):
self.assertEqual(status * 2, scciresult)
self.assertIn("OpCodeExt 0x{0}: {1} ({2})\n".format(format(opcode1, 'X'), datastr, status) +
"OpCodeExt 0x{0}: {1} ({2})".format(format(opcode2, 'X'), dataint, status), sccicontext)
- self.assertEquals(datastr, sccidata['description'])
- self.assertEquals(dataint, sccidata['enabled'])
+ self.assertEqual(datastr, sccidata['description'])
+ self.assertEqual(dataint, sccidata['enabled'])
def test__get_scciresultlist__bad_overall_status(self):
status = 0
@@ -286,9 +288,9 @@ def test__get_scciresultlist__bad_overall_status(self):
sccidata, scciresult, sccicontext = \
irmc_scci_utils.get_scciresultlist(sccireturndata, self.userdata, self.param_scci_map)
self.assertEqual(status, scciresult)
- self.assertEquals("", sccicontext)
- self.assertEquals("", sccidata['description'])
- self.assertEquals("", sccidata['enabled'])
+ self.assertEqual("", sccicontext)
+ self.assertEqual("", sccidata['description'])
+ self.assertEqual("", sccidata['enabled'])
def test__add_scci_command__all_is_well_get(self):
scci_type = "GET"
@@ -300,7 +302,7 @@ def test__add_scci_command__all_is_well_get(self):
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__add_scci_command__all_is_well_set_string(self):
scci_type = "SET"
@@ -313,7 +315,7 @@ def test__add_scci_command__all_is_well_set_string(self):
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__add_scci_command__all_is_well_set_integer(self):
scci_type = "SET"
@@ -326,7 +328,7 @@ def test__add_scci_command__all_is_well_set_integer(self):
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__add_scci_command__all_is_well_create_integer(self):
scci_type = "CREATE"
@@ -339,7 +341,7 @@ def test__add_scci_command__all_is_well_create_integer(self):
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__add_scci_command__all_is_well_delete_integer(self):
scci_type = "CREATE"
@@ -352,7 +354,7 @@ def test__add_scci_command__all_is_well_delete_integer(self):
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
expect = etree.tostring(objectify.fromstring(expectedxml))
result = etree.tostring(objectify.fromstring(body))
- self.assertEquals(expect, result)
+ self.assertEqual(expect, result)
def test__add_scci_command__all_is_well_set_empty(self):
scci_type = "SET"
@@ -360,7 +362,7 @@ def test__add_scci_command__all_is_well_set_empty(self):
data = None
expectedxml = ""
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
- self.assertEquals(expectedxml, body)
+ self.assertEqual(expectedxml, body)
def test__add_scci_command__all_is_well_create_empty(self):
scci_type = "CREATE"
@@ -368,7 +370,7 @@ def test__add_scci_command__all_is_well_create_empty(self):
data = None
expectedxml = ""
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
- self.assertEquals(expectedxml, body)
+ self.assertEqual(expectedxml, body)
def test__add_scci_command__bad_opcode(self):
scci_type = "CREATE"
@@ -376,7 +378,7 @@ def test__add_scci_command__bad_opcode(self):
data = ""
expectedxml = ""
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
- self.assertEquals(expectedxml, body)
+ self.assertEqual(expectedxml, body)
def test__add_scci_command__bad_opcode_type(self):
scci_type = "UNKNOWN"
@@ -384,19 +386,19 @@ def test__add_scci_command__bad_opcode_type(self):
data = ""
expectedxml = ""
body = irmc_scci_utils.add_scci_command(scci_type, self.param_scci_map, scci_text, 0, data)
- self.assertEquals(expectedxml, body)
+ self.assertEqual(expectedxml, body)
def test__get_key_for_value__all_is_well(self):
mydict = {"0": "zero", "1": "one", "2": "two"}
myvalue = "two"
mykey = irmc_scci_utils.get_key_for_value(myvalue, mydict)
- self.assertEquals("2", mykey)
+ self.assertEqual("2", mykey)
def test__get_key_for_value__no_value(self):
mydict = {"0": "zero", "1": "one", "2": "two"}
myvalue = None
mykey = irmc_scci_utils.get_key_for_value(myvalue, mydict)
- self.assertEquals("", mykey)
+ self.assertEqual("", mykey)
def test__get_key_for_value__bad_value(self):
mydict = {"0": "zero", "1": "one", "2": "two"}
diff --git a/tests/test_irmc_upload_file.py b/tests/test_irmc_upload_file.py
new file mode 100644
index 0000000..c96f31a
--- /dev/null
+++ b/tests/test_irmc_upload_file.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+import builtins
+try: # check whether python knows about '__builtin__'
+ __builtin__.open
+except NameError: # no, so it is python3, use 'builtins' instead
+ __builtin__ = builtins
+
+import requests
+from requests.exceptions import Timeout
+import mock
+
+from ansible.compat.tests import unittest
+from ansible.compat.tests.mock import create_autospec, patch
+from ansible.module_utils.basic import AnsibleModule
+
+from module_utils import irmc_upload_file
+
+
+class TestIrmcUploadFile(unittest.TestCase):
+
+ # preparing the tests
+ def setUp(self):
+ mod_cls = create_autospec(AnsibleModule)
+ mod = mod_cls.return_value
+ mod.params = dict(
+ irmc_url="irmc_dns_or_ip",
+ irmc_username="admin",
+ irmc_password="admin",
+ validate_certs=True
+ )
+
+ mockdata = mock.Mock()
+ mockdata.reason = "mocked error reason"
+ mockdata.bad_return = {'Data': 'mockdata', 'error': {'message': mockdata.reason}}
+ mockdata.json.return_value = {'Data': 'mockdata'}
+ mockdata.status_code = 200
+
+ # unittest.TestCase.setUp(self)
+ self.mod = mod
+ self.mockdata = mockdata
+ self.url = "https://{0}/redfish_path".format(mod.params['irmc_url'])
+ self.mockjson = {"Level0Key": "MockLevel0Data",
+ "Level1": {"Level1Key": "MockLevel1Data",
+ "Level2": {"Level2Key": "MockLevel2Data",
+ "Level3": {"Level3Key": "MockLevel3Data",
+ "Level4": {"Level4Key": "MockLevel4Data",
+ "Level5": {"Level5Key": "MockLevel5Data"}}}}}}
+
+ # ending the test
+ def tearDown(self):
+ self.mockdata.dispose()
+ self.mockdata = None
+
+ @patch.object(requests.Session, 'post')
+ @patch("__builtin__.open", mock.mock_open(read_data="data"))
+ def test__irmc_redfish_post_file__all_is_well(self, post):
+ requests.Session.post.return_value = self.mockdata
+ open.return_value = "some filedata"
+ status, data, msg = irmc_upload_file.irmc_redfish_post_file(self.mod, "redfish_path", "filename")
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertEqual(self.mockdata.json.return_value, data.json.return_value)
+ self.assertEqual("OK", msg)
+
+ @patch.object(requests.Session, 'post')
+ @patch("__builtin__.open", mock.mock_open(read_data="data"))
+ def test__irmc_redfish_post_file__no_file(self, post):
+ requests.Session.post.return_value = self.mockdata
+ open.side_effect = Timeout()
+ self.mockdata.status_code = 89
+ status, data, msg = irmc_upload_file.irmc_redfish_post_file(self.mod, "redfish_path", "nofile")
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertIn("Traceback", str(data))
+ self.assertIn("Could not read file at", msg)
+
+ @patch.object(requests.Session, 'post')
+ @patch("__builtin__.open", mock.mock_open(read_data="data"))
+ def test__irmc_redfish_post_file__bad_status_with_reason(self, post):
+ requests.Session.post.return_value = self.mockdata
+ open.return_value = "some filedata"
+ self.mockdata.json.return_value = self.mockdata.bad_return
+ self.mockdata.status_code = 100
+ status, data, msg = irmc_upload_file.irmc_redfish_post_file(self.mod, "redfish_path", "filename")
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertEqual(self.mockdata.json.return_value, data.json.return_value)
+ self.assertEqual("POST request was not successful (" + self.url + "): " + self.mockdata.reason, msg)
+
+ @patch.object(requests.Session, 'post')
+ @patch("__builtin__.open", mock.mock_open(read_data="data"))
+ def test__irmc_redfish_post_file__bad_status_without_reason(self, post):
+ requests.Session.post.return_value = self.mockdata
+ open.return_value = "some filedata"
+ self.mockdata.status_code = 100
+ status, data, msg = irmc_upload_file.irmc_redfish_post_file(self.mod, "redfish_path", "filename")
+ self.assertEqual(self.mockdata.status_code, status)
+ self.assertEqual(self.mockdata.json.return_value, data.json.return_value)
+ self.assertEqual("POST request was not successful (" + self.url + ").", msg)
+
+ # POST exception mock does not work here for unknown reason (2 try/except blocks?)
+ # @patch.object(requests.Session, 'post')
+ # @patch("__builtin__.open", mock.mock_open(read_data="data"))
+ # def test__irmc_redfish_post_file__exception(self, post):
+ # requests.Session.post.side_effect = Timeout()
+ # open.return_value = "some filedata"
+ # status, data, msg = irmc_upload_file.irmc_redfish_post_file(self.mod, "redfish_path", "filename")
+ # self.assertEqual(99, status)
+ # self.assertIn("Traceback", str(data))
+ # self.assertIn("POST request encountered exception (" + self.url + ")", msg)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_irmc_utils.py b/tests/test_irmc_utils.py
new file mode 100644
index 0000000..7946dd4
--- /dev/null
+++ b/tests/test_irmc_utils.py
@@ -0,0 +1,215 @@
+#!/usr/bin/python
+
+# FUJITSU LIMITED
+# Copyright 2018 FUJITSU LIMITED
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+from builtins import str
+
+import mock
+
+from ansible.compat.tests import unittest
+from ansible.compat.tests.mock import create_autospec
+from ansible.module_utils.basic import AnsibleModule
+
+from module_utils import irmc_utils
+
+
+class TestIrmcUtils(unittest.TestCase):
+
+ # preparing the tests
+ def setUp(self):
+ mod_cls = create_autospec(AnsibleModule)
+ mod = mod_cls.return_value
+ mod.params = dict(
+ irmc_url="irmc_dns_or_ip",
+ irmc_username="admin",
+ irmc_password="admin",
+ validate_certs=True
+ )
+
+ mockdata = mock.Mock()
+ mockdata.reason = "mockdata error reason"
+ mockdata.content = ""
+ mockdata.status_code = 200
+
+ # unittest.TestCase.setUp(self)
+ self.mod = mod
+ self.mockdata = mockdata
+ self.url = "http://{0}/config".format(mod.params['irmc_url'])
+
+ # ending the test
+ def tearDown(self):
+ self.mockdata.dispose()
+ self.mockdata = None
+
+ def test__compare_irmc_profile__same_type_identical(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = myprofile1
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(True, cval)
+ self.assertEqual(myclist, clist)
+
+ def test__compare_irmc_profile__same_type_dict_key_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"eins": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("missing keys 'one', found keys 'eins'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile__same_type_dict_value_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"one": 10, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("'1' != '10'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_list__same_type_list_value_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"eight": 8}, {"six": 6}]}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("missing keys 'eight', found keys 'five'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile__same_type_dict_length_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"one": 1, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("dict len '3' != dict len '2' ", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile__same_type_list_length_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"six": 6}]}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("list len '3' != list len '2' ", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile__list_type_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = {"one": 1, "two": 2, "three": {"four": 4, "seven": 7}}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ result = " ".join(str(x) for x in clist).replace("class", "type")
+ self.assertEqual(False, cval)
+ self.assertIn("type '' != type ''", result)
+
+ def test__compare_irmc_profile__dict_type_different(self):
+ myprofile1 = {"one": 1, "two": 2, "three": [{"four": 4, "seven": 7}, {"five": 5}, {"six": 6}]}
+ myprofile2 = [{"one": 1, "two": 2, "three": [{"four": 4}, {"seven": 7}, {"five": 5}, {"six": 6}]}]
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile(myprofile1, myprofile2, "", myclist)
+ result = " ".join(str(x) for x in clist).replace("class", "type")
+ self.assertEqual(False, cval)
+ self.assertIn("type '' != type ''", result)
+
+ def test__compare_irmc_profile_dict__same_type_identical(self):
+ mydict1 = {"one": 1, "two": 2, "three": 3}
+ mydict2 = mydict1
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_dict(mydict1, mydict2, "", myclist)
+ self.assertEqual(True, cval)
+ self.assertEqual(myclist, clist)
+
+ def test__compare_irmc_profile_dict__same_type_key_different(self):
+ mydict1 = {"one": 1, "two": 2, "three": 3}
+ mydict2 = {"four": 1, "two": 2, "three": 3}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_dict(mydict1, mydict2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("missing keys 'one', found keys 'four'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_dict__same_type_value_different(self):
+ mydict1 = {"one": 1, "two": 2, "three": 3}
+ mydict2 = {"one": 4, "two": 2, "three": 3}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_dict(mydict1, mydict2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("'1' != '4'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_dict__same_type_length_different(self):
+ mydict1 = {"one": 1, "two": 2, "three": 3}
+ mydict2 = {"one": 1, "two": 2, "three": 3, "four": 4}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_dict(mydict1, mydict2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("dict len '3' != dict len '4' ", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_dict__type_different(self):
+ mydict1 = {"one": 1, "two": 2, "three": 3}
+ mydict2 = ["one", "two", "three"]
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_dict(mydict1, mydict2, "", myclist)
+ result = " ".join(str(x) for x in clist).replace("class", "type")
+ self.assertEqual(False, cval)
+ self.assertIn("type '' != type ''", result)
+
+ def test__compare_irmc_profile_list__same_type_identical(self):
+ mylist1 = ["one", "two", "three"]
+ mylist2 = mylist1
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_list(mylist1, mylist2, "", myclist)
+ self.assertEqual(True, cval)
+ self.assertEqual(myclist, clist)
+
+ def test__compare_irmc_profile_list__same_type_value_different(self):
+ mylist1 = ["one", "two", "three"]
+ mylist2 = ["four", "two", "three"]
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_list(mylist1, mylist2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("'four' != 'one'", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_list__same_type_length_different(self):
+ mylist1 = ["one", "two", "three"]
+ mylist2 = ["one", "two", "three", "four"]
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_list(mylist1, mylist2, "", myclist)
+ self.assertEqual(False, cval)
+ self.assertIn("list len '3' != list len '4' ", " ".join(str(x) for x in clist))
+
+ def test__compare_irmc_profile_list__type_different(self):
+ mylist1 = ["one", "two", "three"]
+ mylist2 = {"one": 1, "two": 2, "three": 3}
+ myclist = []
+ cval, clist = irmc_utils.compare_irmc_profile_list(mylist1, mylist2, "", myclist)
+ result = " ".join(str(x) for x in clist).replace("class", "type")
+ self.assertEqual(False, cval)
+ self.assertIn("type '' != type ''", result)
+
+ def test__is_final_type__list(self):
+ mylist = ["one", "two", "three"]
+ myval = irmc_utils.is_final_type(mylist)
+ self.assertEqual(False, myval)
+
+ def test__is_final_type__dict(self):
+ mydict = {"one": 1, "two": 2, "three": 3}
+ myval = irmc_utils.is_final_type(mydict)
+ self.assertEqual(False, myval)
+
+ def test__is_final_type__string(self):
+ mystring = "one, two, three"
+ myval = irmc_utils.is_final_type(mystring)
+ self.assertEqual(True, myval)
+
+ def test__is_final_type__int(self):
+ myint = 123
+ myval = irmc_utils.is_final_type(myint)
+ self.assertEqual(True, myval)
+
+ def test__is_final_type__bool(self):
+ mybool = False
+ myval = irmc_utils.is_final_type(mybool)
+ self.assertEqual(True, myval)
+
+
+if __name__ == '__main__':
+ unittest.main()