Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
# Collections Plugins Directory
This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that
is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that
would contain module utils and modules respectively.
Here is an example directory of the majority of plugins currently supported by Ansible:
```
└── plugins
├── action
├── become
├── cache
├── callback
├── cliconf
├── connection
├── filter
├── httpapi
├── inventory
├── lookup
├── module_utils
├── modules
├── netconf
├── shell
├── strategy
├── terminal
├── test
└── vars
```
A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html).

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# (c) 2016 Red Hat Inc.
# (c) 2017 Cisco Systems Inc.
#
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
#
class ModuleDocFragment(object):
# Cisco Intersight doc fragment
DOCUMENTATION = '''
options:
api_private_key:
description:
- 'Filename (absolute path) or string of PEM formatted private key data to be used for Intersight API authentication.'
- If a string is used, Ansible vault should be used to encrypt string data.
- "Ex. ansible-vault encrypt_string --vault-id tme@/Users/dsoper/Documents/vault_password_file '-----BEGIN EC PRIVATE KEY-----"
- " <your private key data>"
- " -----END EC PRIVATE KEY-----'"
- If not set, the value of the INTERSIGHT_API_PRIVATE_KEY environment variable is used.
type: str
required: yes
api_uri:
description:
- URI used to access the Intersight API.
- If not set, the value of the INTERSIGHT_API_URI environment variable is used.
type: str
default: https://intersight.com/api/v1
api_key_id:
description:
- Public API Key ID associated with the private key.
- If not set, the value of the INTERSIGHT_API_KEY_ID environment variable is used.
type: str
required: yes
validate_certs:
description:
- Boolean control for verifying the api_uri TLS certificate
type: bool
default: yes
use_proxy:
description:
- If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts.
type: bool
default: yes
'''

View File

@@ -0,0 +1,471 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# (c) 2016 Red Hat Inc.
# (c) 2020 Cisco Systems Inc.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Intersight REST API Module
# Author: Matthew Garrett
# Contributors: David Soper, Chris Gascoigne, John McDonough
from base64 import b64encode
from email.utils import formatdate
import re
import json
import hashlib
from ansible.module_utils.six import iteritems
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.basic import env_fallback
try:
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding, ec
from cryptography.hazmat.backends import default_backend
HAS_CRYPTOGRAPHY = True
except ImportError:
HAS_CRYPTOGRAPHY = False
intersight_argument_spec = dict(
api_private_key=dict(fallback=(env_fallback, ['INTERSIGHT_API_PRIVATE_KEY']), type='path', required=True, no_log=True),
api_uri=dict(fallback=(env_fallback, ['INTERSIGHT_API_URI']), type='str', default='https://intersight.com/api/v1'),
api_key_id=dict(fallback=(env_fallback, ['INTERSIGHT_API_KEY_ID']), type='str', required=True),
validate_certs=dict(type='bool', default=True),
use_proxy=dict(type='bool', default=True),
)
def get_sha256_digest(data):
"""
Generates a SHA256 digest from a String.
:param data: data string set by user
:return: instance of digest object
"""
digest = hashlib.sha256()
digest.update(data.encode())
return digest
def prepare_str_to_sign(req_tgt, hdrs):
"""
Concatenates Intersight headers in preparation to be signed
:param req_tgt : http method plus endpoint
:param hdrs: dict with header keys
:return: concatenated header authorization string
"""
ss = ""
ss = ss + "(request-target): " + req_tgt + "\n"
length = len(hdrs.items())
i = 0
for key, value in hdrs.items():
ss = ss + key.lower() + ": " + value
if i < length - 1:
ss = ss + "\n"
i += 1
return ss
def get_gmt_date():
"""
Generated a GMT formatted Date
:return: current date
"""
return formatdate(timeval=None, localtime=False, usegmt=True)
def compare_lists(expected_list, actual_list):
if len(expected_list) != len(actual_list):
# mismatch if list lengths aren't equal
return False
for expected, actual in zip(expected_list, actual_list):
# if compare_values returns False, stop the loop and return
if not compare_values(expected, actual):
return False
# loop complete with all items matching
return True
def compare_values(expected, actual):
try:
if isinstance(expected, list) and isinstance(actual, list):
return compare_lists(expected, actual)
for (key, value) in iteritems(expected):
if re.search(r'P(ass)?w(or)?d', key) or key not in actual:
# do not compare any password related attributes or attributes that are not in the actual resource
continue
if not compare_values(value, actual[key]):
return False
# loop complete with all items matching
return True
except (AttributeError, TypeError):
# if expected and actual != expected:
if actual != expected:
return False
return True
class IntersightModule():
def __init__(self, module):
self.module = module
self.result = dict(changed=False)
if not HAS_CRYPTOGRAPHY:
self.module.fail_json(msg='cryptography is required for this module')
self.host = self.module.params['api_uri']
self.public_key = self.module.params['api_key_id']
try:
with open(self.module.params['api_private_key'], 'r') as f:
self.private_key = f.read()
except (FileNotFoundError, OSError):
self.private_key = self.module.params['api_private_key']
self.digest_algorithm = ''
self.response_list = []
def get_sig_b64encode(self, data):
"""
Generates a signed digest from a String
:param digest: string to be signed & hashed
:return: instance of digest object
"""
# Python SDK code: Verify PEM Pre-Encapsulation Boundary
r = re.compile(r"\s*-----BEGIN (.*)-----\s+")
m = r.match(self.private_key)
if not m:
raise ValueError("Not a valid PEM pre boundary")
pem_header = m.group(1)
key = serialization.load_pem_private_key(self.private_key.encode(), None, default_backend())
if pem_header == 'RSA PRIVATE KEY':
sign = key.sign(data.encode(), padding.PKCS1v15(), hashes.SHA256())
self.digest_algorithm = 'rsa-sha256'
elif pem_header == 'EC PRIVATE KEY':
sign = key.sign(data.encode(), ec.ECDSA(hashes.SHA256()))
self.digest_algorithm = 'hs2019'
else:
raise Exception("Unsupported key: {0}".format(pem_header))
return b64encode(sign)
def get_auth_header(self, hdrs, signed_msg):
"""
Assmebled an Intersight formatted authorization header
:param hdrs : object with header keys
:param signed_msg: base64 encoded sha256 hashed body
:return: concatenated authorization header
"""
auth_str = "Signature"
auth_str = auth_str + " " + "keyId=\"" + self.public_key + "\"," + "algorithm=\"" + self.digest_algorithm + "\","
auth_str = auth_str + "headers=\"(request-target)"
for key, dummy in hdrs.items():
auth_str = auth_str + " " + key.lower()
auth_str = auth_str + "\""
auth_str = auth_str + "," + "signature=\"" + signed_msg.decode('ascii') + "\""
return auth_str
def get_moid_by_name(self, resource_path, target_name):
"""
Retrieve an Intersight object moid by name
:param resource_path: intersight resource path e.g. '/ntp/Policies'
:param target_name: intersight object name
:return: json http response object
"""
query_params = {
"$filter": "Name eq '{0}'".format(target_name)
}
options = {
"http_method": "GET",
"resource_path": resource_path,
"query_params": query_params
}
get_moid = self.intersight_call(**options)
if get_moid.json()['Results'] is not None:
located_moid = get_moid.json()['Results'][0]['Moid']
else:
raise KeyError('Intersight object with name "{0}" not found!'.format(target_name))
return located_moid
def call_api(self, **options):
"""
Call the Intersight API and check for success status
:param options: options dict with method and other params for API call
:return: json http response object
"""
try:
response, info = self.intersight_call(**options)
if not re.match(r'2..', str(info['status'])):
raise RuntimeError(info['status'], info['msg'], info['body'])
except Exception as e:
self.module.fail_json(msg="API error: %s " % str(e))
response_data = response.read()
if len(response_data) > 0:
resp_json = json.loads(response_data)
resp_json['trace_id'] = info.get('x-starship-traceid')
return resp_json
return {}
def intersight_call(self, http_method="", resource_path="", query_params=None, body=None, moid=None, name=None):
"""
Invoke the Intersight API
:param resource_path: intersight resource path e.g. '/ntp/Policies'
:param query_params: dictionary object with query string parameters as key/value pairs
:param body: dictionary object with intersight data
:param moid: intersight object moid
:param name: intersight object name
:return: json http response object
"""
target_host = urlparse(self.host).netloc
target_path = urlparse(self.host).path
query_path = ""
method = http_method.upper()
bodyString = ""
# Verify an accepted HTTP verb was chosen
if(method not in ['GET', 'POST', 'PATCH', 'DELETE']):
raise ValueError('Please select a valid HTTP verb (GET/POST/PATCH/DELETE)')
# Verify the resource path isn't empy & is a valid <str> object
if(resource_path != "" and not (resource_path, str)):
raise TypeError('The *resource_path* value is required and must be of type "<str>"')
# Verify the query parameters isn't empy & is a valid <dict> object
if(query_params is not None and not isinstance(query_params, dict)):
raise TypeError('The *query_params* value must be of type "<dict>"')
# Verify the MOID is not null & of proper length
if(moid is not None and len(moid.encode('utf-8')) != 24):
raise ValueError('Invalid *moid* value!')
# Check for query_params, encode, and concatenate onto URL
if query_params:
query_path = "?" + urlencode(query_params)
# Handle PATCH/DELETE by Object "name" instead of "moid"
if method in ('PATCH', 'DELETE'):
if moid is None:
if name is not None:
if isinstance(name, str):
moid = self.get_moid_by_name(resource_path, name)
else:
raise TypeError('The *name* value must be of type "<str>"')
else:
raise ValueError('Must set either *moid* or *name* with "PATCH/DELETE!"')
# Check for moid and concatenate onto URL
if moid is not None:
resource_path += "/" + moid
# Check for GET request to properly form body
if method != "GET":
bodyString = json.dumps(body)
# Concatenate URLs for headers
target_url = self.host + resource_path + query_path
request_target = method.lower() + " " + target_path + resource_path + query_path
# Get the current GMT Date/Time
cdate = get_gmt_date()
# Generate the body digest
body_digest = get_sha256_digest(bodyString)
b64_body_digest = b64encode(body_digest.digest())
# Generate the authorization header
auth_header = {
'Host': target_host,
'Date': cdate,
'Digest': "SHA-256=" + b64_body_digest.decode('ascii'),
}
string_to_sign = prepare_str_to_sign(request_target, auth_header)
b64_signed_msg = self.get_sig_b64encode(string_to_sign)
auth_header = self.get_auth_header(auth_header, b64_signed_msg)
# Generate the HTTP requests header
request_header = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Host': '{0}'.format(target_host),
'Date': '{0}'.format(cdate),
'Digest': 'SHA-256={0}'.format(b64_body_digest.decode('ascii')),
'Authorization': '{0}'.format(auth_header),
}
response, info = fetch_url(self.module, target_url, data=bodyString, headers=request_header, method=method, use_proxy=self.module.params['use_proxy'])
return response, info
def get_resource(self, resource_path, query_params, return_list=False):
'''
GET a resource and return the 1st element found or the full Results list
'''
options = {
'http_method': 'get',
'resource_path': resource_path,
'query_params': query_params,
}
response = self.call_api(**options)
if response.get('Results'):
if return_list:
self.result['api_response'] = response['Results']
else:
# return the 1st list element
self.result['api_response'] = response['Results'][0]
self.result['trace_id'] = response.get('trace_id')
def configure_resource(self, moid, resource_path, body, query_params, update_method=''):
if not self.module.check_mode:
if moid and update_method != 'post':
# update the resource - user has to specify all the props they want updated
options = {
'http_method': 'patch',
'resource_path': resource_path,
'body': body,
'moid': moid,
}
response_dict = self.call_api(**options)
if response_dict.get('Results'):
# return the 1st element in the results list
self.result['api_response'] = response_dict['Results'][0]
self.result['trace_id'] = response_dict.get('trace_id')
else:
# create the resource
options = {
'http_method': 'post',
'resource_path': resource_path,
'body': body,
}
response_dict = self.call_api(**options)
if response_dict:
self.result['api_response'] = response_dict
self.result['trace_id'] = response_dict.get('trace_id')
elif query_params:
# POSTs may not return any data.
# Get the current state of the resource if query_params.
self.get_resource(
resource_path=resource_path,
query_params=query_params,
)
self.result['changed'] = True
def delete_resource(self, moid, resource_path):
# delete resource and create empty api_response
if not self.module.check_mode:
options = {
'http_method': 'delete',
'resource_path': resource_path,
'moid': moid,
}
resp = self.call_api(**options)
self.result['api_response'] = {}
self.result['trace_id'] = resp.get('trace_id')
self.result['changed'] = True
def configure_policy_or_profile(self, resource_path):
# Configure (create, update, or delete) the policy or profile
organization_moid = None
# GET Organization Moid
self.get_resource(
resource_path='/organization/Organizations',
query_params={
'$filter': "Name eq '" + self.module.params['organization'] + "'",
'$select': 'Moid',
},
)
if self.result['api_response'].get('Moid'):
# resource exists and moid was returned
organization_moid = self.result['api_response']['Moid']
self.result['api_response'] = {}
# Get the current state of the resource
filter_str = "Name eq '" + self.module.params['name'] + "'"
filter_str += "and Organization.Moid eq '" + organization_moid + "'"
self.get_resource(
resource_path=resource_path,
query_params={
'$filter': filter_str,
'$expand': 'Organization',
}
)
moid = None
resource_values_match = False
if self.result['api_response'].get('Moid'):
# resource exists and moid was returned
moid = self.result['api_response']['Moid']
if self.module.params['state'] == 'present':
resource_values_match = compare_values(self.api_body, self.result['api_response'])
else: # state == 'absent'
self.delete_resource(
moid=moid,
resource_path=resource_path,
)
moid = None
if self.module.params['state'] == 'present' and not resource_values_match:
# remove read-only Organization key
self.api_body.pop('Organization')
if not moid:
# Organization must be set, but can't be changed after initial POST
self.api_body['Organization'] = {
'Moid': organization_moid,
}
self.configure_resource(
moid=moid,
resource_path=resource_path,
body=self.api_body,
query_params={
'$filter': filter_str
}
)
if self.result['api_response'].get('Moid'):
# resource exists and moid was returned
moid = self.result['api_response']['Moid']
return moid

View File

@@ -0,0 +1,495 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_boot_order_policy
short_description: Boot Order policy configuration for Cisco Intersight
description:
- Boot Order policy configuration for Cisco Intersight.
- Used to configure Boot Order servers and timezone settings on Cisco Intersight managed devices.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the Boot Order policy.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
type: list
description:
description:
- The user-defined description of the Boot Order policy.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
configured_boot_mode:
description:
- Sets the BIOS boot mode.
- UEFI uses the GUID Partition Table (GPT) whereas Legacy mode uses the Master Boot Record (MBR) partitioning scheme.
choices: [Legacy, Uefi]
default: Legacy
uefi_enable_secure_boot:
description:
- Secure boot enforces that device boots using only software that is trusted by the Original Equipment Manufacturer (OEM).
- Option is only used if configured_boot_mode is set to Uefi.
type: bool
default: false
boot_devices:
description:
- List of Boot Devices configured on the endpoint.
type: list
suboptions:
enabled:
description:
- Specifies if the boot device is enabled or disabled.
type: bool
default: true
device_type:
description:
- Device type used with this boot option.
- Choices are based on each device title in the API schema.
choices: [iSCSI, Local CDD, Local Disk, NVMe, PCH Storage, PXE, SAN, SD Card, UEFI Shell, USB, Virtual Media]
required: true
device_name:
description:
- A name that helps identify a boot device.
- It can be any string that adheres to the following constraints.
- It should start and end with an alphanumeric character.
- It can have underscores and hyphens.
- It cannot be more than 30 characters.
required: true
network_slot:
description:
- The slot id of the controller for the iscsi and pxe device.
- Option is used when device_type is iscsi and pxe.
choices: [1 - 255, MLOM, L, L1, L2, OCP]
port:
description:
- The port id of the controller for the iscsi and pxe device.
- Option is used when device_type is iscsi and pxe.
- The port id need to be an integer from 0 to 255.
controller_slot:
description:
- The slot id of the controller for the local disk device.
- Option is used when device_type is local_disk.
choices: [1-255, M, HBA, SAS, RAID, MRAID, MSTOR-RAID]
bootloader_name:
description:
- Details of the bootloader to be used during boot from local disk.
- Option is used when device_type is local_disk and configured_boot_mode is Uefi.
bootloader_description:
description:
- Details of the bootloader to be used during boot from local disk.
- Option is used when device_type is local_disk and configured_boot_mode is Uefi.
bootloader_path:
description:
- Details of the bootloader to be used during boot from local disk.
- Option is used when device_type is local_disk and configured_boot_mode is Uefi.
ip_type:
description:
- The IP Address family type to use during the PXE Boot process.
- Option is used when device_type is pxe.
choices: [None, IPv4, IPv6]
default: None
interface_source:
description:
- Lists the supported Interface Source for PXE device.
- Option is used when device_type is pxe.
choices: [name, mac, port]
default: name
intefrace_name:
description:
- The name of the underlying virtual ethernet interface used by the PXE boot device.
- Option is used when device_type is pxe and interface_source is name.
mac_address:
description:
- The MAC Address of the underlying virtual ethernet interface used by the PXE boot device.
- Option is used when device_type is pxe and interface_source is mac.
sd_card_subtype:
description:
- The subtype for the selected device type.
- Option is used when device_type is sd_card.
choices: [None, flex-util, flex-flash, SDCARD]
default: None
lun:
description:
- The Logical Unit Number (LUN) of the device.
- Option is used when device_type is pch, san and sd_card.
- The LUN need to be an integer from 0 to 255.
usb_subtype:
description:
- The subtype for the selected device type.
- Option is used when device_type is usb.
choices: [None, usb-cd, usb-fdd, usb-hdd]
default: None
virtual_media_subtype:
description:
- The subtype for the selected device type.
- Option is used when device_type is virtual_media.
choices: [None, cimc-mapped-dvd, cimc-mapped-hdd, kvm-mapped-dvd, kvm-mapped-hdd, kvm-mapped-fdd]
default: None
author:
- Tse Kai "Kevin" Chan (@BrightScale)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure Boot Order Policy
cisco.intersight.intersight_boot_order_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: COS-Boot
description: Boot Order policy for COS
tags:
- Key: Site
Value: RCDN
configured_boot_mode: legacy
boot_devices:
- device_type: Local Disk
device_name: Boot-Lun
controller_slot: MRAID
- name: Delete Boot Order Policy
cisco.intersight.intersight_boot_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: COS-Boot
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Name": "COS-Boot",
"ObjectType": "boot.Policy",
"Tags": [
{
"Key": "Site",
"Value": "RCDN"
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec
def main():
boot_device = dict(
enabled=dict(type='bool', default=True),
device_type=dict(
type='str',
choices=[
'iSCSI',
'Local CDD',
'Local Disk',
'NVMe',
'PCH Storage',
'PXE',
'SAN',
'SD Card',
'UEFI Shell',
'USB',
'Virtual Media',
],
required=True,
),
device_name=dict(type='str', required=True),
# iscsi and pxe options
network_slot=dict(type='str', default=''),
port=dict(type='int', default=0),
# local disk options
controller_slot=dict(type='str', default=''),
# bootloader options
bootloader_name=dict(type='str', default=''),
bootloader_description=dict(type='str', default=''),
bootloader_path=dict(type='str', default=''),
# pxe only options
ip_type=dict(
type='str',
choices=[
'None',
'IPv4',
'IPv6'
],
default='None'
),
interface_source=dict(
type='str',
choices=[
'name',
'mac',
'port'
],
default='name'
),
interface_name=dict(type='str', default=''),
mac_address=dict(type='str', defualt=''),
# sd card options
sd_card_subtype=dict(
type='str',
choices=[
'None',
'flex-util',
'flex-flash',
'SDCARD'
],
default='None',
),
# lun for pch, san, sd_card
lun=dict(type='int', default=0),
# usb options
usb_subtype=dict(
type='str',
choices=[
'None',
'usb-cd',
'usb-fdd',
'usb-hdd'
],
default='None',
),
# virtual media options
virtual_media_subtype=dict(
type='str',
choices=[
'None',
'cimc-mapped-dvd',
'cimc-mapped-hdd',
'kvm-mapped-dvd',
'kvm-mapped-hdd',
'kvm-mapped-fdd'
],
default='None',
),
)
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
description=dict(type='str', aliases=['descr'], default=''),
tags=dict(type='list', default=[]),
configured_boot_mode=dict(type='str', choices=['Legacy', 'Uefi'], default='Legacy'),
uefi_enable_secure_boot=dict(type='bool', default=False),
boot_devices=dict(type='list', elements='dict', options=boot_device),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
#
# Argument spec above, resource path, and API body should be the only code changed in each policy module
#
# Resource path used to configure policy
resource_path = '/boot/PrecisionPolicies'
# Define API body used in compares or create
intersight.api_body = {
'Organization': {
'Name': intersight.module.params['organization'],
},
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'ConfiguredBootMode': intersight.module.params['configured_boot_mode'],
"EnforceUefiSecureBoot": intersight.module.params['uefi_enable_secure_boot'],
'BootDevices': [],
}
if intersight.module.params.get('boot_devices'):
for device in intersight.module.params['boot_devices']:
if device['device_type'] == 'iSCSI':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.Iscsi",
"ObjectType": "boot.Iscsi",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Slot": device['network_slot'],
"Port": device['port'],
}
)
elif device['device_type'] == 'Local CDD':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.LocalCDD",
"ObjectType": "boot.LocalCDD",
"Enabled": device['enabled'],
"Name": device['device_name'],
}
)
elif device['device_type'] == 'Local Disk':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.LocalDisk",
"ObjectType": "boot.LocalDisk",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Slot": device['controller_slot'],
"Bootloader": {
"ClassId": "boot.Bootloader",
"ObjectType": "boot.Bootloader",
"Description": device['bootloader_description'],
"Name": device['bootloader_name'],
"Path": device['bootloader_path'],
},
}
)
elif device['device_type'] == 'NVMe':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.NVMe",
"ObjectType": "boot.NVMe",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Bootloader": {
"ClassId": "boot.Bootloader",
"ObjectType": "boot.Bootloader",
"Description": device['bootloader_description'],
"Name": device['bootloader_name'],
"Path": device['bootloader_path'],
},
}
)
elif device['device_type'] == 'PCH Storage':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.PchStorage",
"ObjectType": "boot.PchStorage",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Bootloader": {
"ClassId": "boot.Bootloader",
"ObjectType": "boot.Bootloader",
"Description": device['bootloader_description'],
"Name": device['bootloader_name'],
"Path": device['bootloader_path'],
},
"Lun": device['lun'],
}
)
elif device['device_type'] == 'PXE':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.Pxe",
"ObjectType": "boot.Pxe",
"Enabled": device['enabled'],
"Name": device['device_name'],
"IpType": device['ip_type'],
"InterfaceSource": device['interface_source'],
"Slot": device['network_slot'],
"InterfaceName": device['interface_name'],
"Port": device['port'],
"MacAddress": device['mac_address'],
}
)
elif device['device_type'] == 'SAN':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.San",
"ObjectType": "boot.San",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Lun": device['lun'],
"Slot": device['network_slot'],
"Bootloader": {
"ClassId": "boot.Bootloader",
"ObjectType": "boot.Bootloader",
"Description": device['bootloader_description'],
"Name": device['bootloader_name'],
"Path": device['bootloader_path'],
},
}
)
elif device['device_type'] == 'SD Card':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.SdCard",
"ObjectType": "boot.SdCard",
"Enabled": device['enabled'],
"Name": device['device_name'],
"Lun": device['lun'],
"SubType": device['sd_card_subtype'],
"Bootloader": {
"ClassId": "boot.Bootloader",
"ObjectType": "boot.Bootloader",
"Description": device['bootloader_description'],
"Name": device['bootloader_name'],
"Path": device['bootloader_path'],
},
}
)
elif device['device_type'] == 'UEFI Shell':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.UefiShell",
"ObjectType": "boot.UefiShell",
"Enabled": device['enabled'],
"Name": device['device_name'],
}
)
elif device['device_type'] == 'USB':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.Usb",
"ObjectType": "boot.Usb",
"Enabled": device['enabled'],
"Name": device['device_name'],
"SubType": device['usb_subtype'],
}
)
elif device['device_type'] == 'Virtual Media':
intersight.api_body['BootDevices'].append(
{
"ClassId": "boot.VirtualMedia",
"ObjectType": "boot.VirtualMedia",
"Enabled": device['enabled'],
"Name": device['device_name'],
"SubType": device['virtual_media_subtype'],
}
)
#
# Code below should be common across all policy modules
#
intersight.configure_policy_or_profile(resource_path=resource_path)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,200 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_imc_access_policy
short_description: IMC Access Policy configuration for Cisco Intersight
description:
- IMC Access Policy configuration for Cisco Intersight.
- Used to configure IP addresses and VLAN used for external connectivity to Cisco IMC.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the IMC Access Policy.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
descrption:
description:
- The user-defined description of the IMC access policy.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
vlan_id:
description:
- VLAN to be used for server access over Inband network.
required: true
type: int
ip_pool:
description:
- IP Pool used to assign IP address and other required network settings.
required: true
author:
- David Soper (@dsoper2)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure IMC Access policy
intersight_imc_access_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: sjc02-d23-access
description: IMC access for SJC02 rack D23
tags:
- Site: D23
vlan_id: 131
ip_pool: sjc02-d23-ext-mgmt
- name: Delete IMC Access policy
intersight_imc_access_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: sjc02-d23-access
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Name": "sjc02-d23-access",
"ObjectType": "access.Policy",
"Profiles": [
{
"Moid": "5e4ec7ae77696e2d30840cfc",
"ObjectType": "server.Profile",
},
{
"Moid": "5e84d78777696e2d302ec195",
"ObjectType": "server.Profile",
}
],
"Tags": [
{
"Key": "Site",
"Value": "SJC02"
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec, compare_values
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
description=dict(type='str', aliases=['descr'], default=''),
tags=dict(type='list', default=[]),
vlan_id=dict(type='int', required=True),
ip_pool=dict(type='str', required=True),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
intersight.api_body = {
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'InbandVlan': intersight.module.params['vlan_id'],
'Organization': {
'Name': intersight.module.params['organization'],
},
}
# get the current state of the resource
intersight.get_resource(
resource_path='/access/Policies',
query_params={
'$filter': "Name eq '" + intersight.module.params['name'] + "'",
'$expand': 'Organization',
},
)
moid = None
resource_values_match = False
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
moid = intersight.result['api_response']['Moid']
if module.params['state'] == 'present':
resource_values_match = compare_values(intersight.api_body, intersight.result['api_response'])
else: # state == 'absent'
intersight.delete_resource(
moid=moid,
resource_path='/access/Policies',
)
moid = None
if module.params['state'] == 'present' and not resource_values_match:
# remove read-only Organization key
intersight.api_body.pop('Organization')
if not moid:
# GET Organization Moid
intersight.get_resource(
resource_path='/organization/Organizations',
query_params={
'$filter': "Name eq '" + intersight.module.params['organization'] + "'",
'$select': 'Moid',
},
)
organization_moid = None
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
organization_moid = intersight.result['api_response']['Moid']
# Organization must be set, but can't be changed after initial POST
intersight.api_body['Organization'] = {
'Moid': organization_moid,
}
intersight.configure_resource(
moid=moid,
resource_path='/access/Policies',
body=intersight.api_body,
query_params={
'$filter': "Name eq '" + intersight.module.params['name'] + "'",
},
)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,117 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_info
short_description: Gather information about Intersight
description:
- Gathers information about servers in L(Cisco Intersight,https://intersight.com).
- This module was called C(intersight_facts) before Ansible 2.9. The usage did not change.
extends_documentation_fragment: intersight
options:
server_names:
description:
- Server names to retrieve information from.
- An empty list will return all servers.
type: list
required: yes
author:
- David Soper (@dsoper2)
- CiscoUcs (@CiscoUcs)
version_added: '2.8'
'''
EXAMPLES = r'''
- name: Get info for all servers
intersight_info:
api_private_key: ~/Downloads/SecretKey.txt
api_key_id: 64612d300d0982/64612d300d0b00/64612d300d3650
server_names:
- debug:
msg: "server name {{ item.Name }}, moid {{ item.Moid }}"
loop: "{{ intersight_servers }}"
when: intersight_servers is defined
- name: Get info for servers by name
intersight_info:
api_private_key: ~/Downloads/SecretKey.txt
api_key_id: 64612d300d0982/64612d300d0b00/64612d300d3650
server_names:
- SJC18-L14-UCS1-1
- debug:
msg: "server moid {{ intersight_servers[0].Moid }}"
when: intersight_servers[0] is defined
'''
RETURN = r'''
intersight_servers:
description: A list of Intersight Servers. See L(Cisco Intersight,https://intersight.com/apidocs) for details.
returned: always
type: complex
contains:
Name:
description: The name of the server.
returned: always
type: str
sample: SJC18-L14-UCS1-1
Moid:
description: The unique identifier of this Managed Object instance.
returned: always
type: str
sample: 5978bea36ad4b000018d63dc
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec
def get_servers(module, intersight):
query_list = []
if module.params['server_names']:
for server in module.params['server_names']:
query_list.append("Name eq '%s'" % server)
query_str = ' or '.join(query_list)
options = {
'http_method': 'get',
'resource_path': '/compute/PhysicalSummaries',
'query_params': {
'$filter': query_str,
'$top': 1000,
}
}
response_dict = intersight.call_api(**options)
return response_dict.get('Results')
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
server_names=dict(type='list', required=True),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
if module._name == 'intersight_facts':
module.deprecate("The 'intersight_facts' module has been renamed to 'intersight_info'", version='2.13')
intersight = IntersightModule(module)
# one API call returning all requested servers
module.exit_json(intersight_servers=get_servers(module, intersight))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,363 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_local_user_policy
short_description: Local User Policy configuration for Cisco Intersight
description:
- Local User Policy configuration for Cisco Intersight.
- Used to configure local users on endpoint devices.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the Local User Policy.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
description:
description:
- The user-defined description of the Local User policy.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
enforce_strong_password:
description:
- If true, enables a strong password policy.
- Strong password requirements:.
- A. The password must have a minimum of 8 and a maximum of 20 characters.
- B. The password must not contain the User's Name.
- C. The password must contain characters from three of the following four categories.
- 1) English uppercase characters (A through Z).
- 2) English lowercase characters (a through z).
- 3) Base 10 digits (0 through 9).
- 4) Non-alphabetic characters (! , @, '#', $, %, ^, &, *, -, _, +, =).
type: bool
default: true
enable_password_expiry:
description:
- Enables password expiry on the endpoint.
type: bool
default: false
password_history:
description:
- Specifies number of times a password cannot repeat when changed (value between 0 and 5).
- Entering 0 disables this option.
type: int
default: 5
local_users:
description:
- List of local users on the endpoint.
- An admin user already exists on the endpoint.
- Add the admin user here only if you want to change the password, or enable or disable the user.
- To add admin user, provide a username as 'admin', select the admin user role, and then proceed.
suboptions:
username:
description:
- Name of the user created on the endpoint.
required: true
enable:
description:
- Enable or disable the user.
type: bool
default: true
role:
description:
- Roles associated with the user on the endpoint.
choices: [admin, readonly, user]
required: true
password:
description:
- Valid login password of the user.
required: true
purge:
description:
- The purge argument instructs the module to consider the resource definition absolute.
- If true, any previously configured usernames will be removed from the policy with the exception of the `admin` user which cannot be deleted.
default: false
always_update_password:
description:
- Since passwords are not returned by the API and are encrypted on the endpoint, this option will instruct the module when to change the password.
- If true, the password for each user will always be updated in the policy.
- If false, the password will be updated only if the user is created.
default: false
author:
- David Soper (@dsoper2)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure Local User policy
intersight_local_user_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: guest-admin
tags:
- Key: username
Value: guest
description: User named guest with admin role
local_users:
- username: guest
role: admin
password: vault_guest_password
- username: reader
role: readonly
password: vault_reader_password
- name: Delete Local User policy
intersight_local_user_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: guest-admin
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Description": "User named guest with admin role",
"EndPointUserRoles": [
{
"ChangePassword": true,
"Enabled": true
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec, compare_values
def main():
local_user = dict(
username=dict(type='str', required=True),
enable=dict(type='bool', default=True),
role=dict(type='str', choices=['admin', 'readonly', 'user'], required=True),
password=dict(type='str', required=True, no_log=True),
)
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
description=dict(type='str', aliases=['descr'], default=''),
tags=dict(type='list', default=[]),
enforce_strong_password=dict(type='bool', default=True, no_log=False),
enable_password_expiry=dict(type='bool', default=False, no_log=False),
password_history=dict(type='int', default=5, no_log=False),
local_users=dict(type='list', elements='dict', options=local_user, default=[]),
purge=dict(type='bool', default=False),
always_update_password=dict(type='bool', default=False, no_log=False),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
# get the current state of the resource
intersight.get_resource(
resource_path='/iam/EndPointUserPolicies',
query_params={
'$filter': "Name eq '" + intersight.module.params['name'] + "'",
'$expand': 'EndPointUserRoles($expand=EndPointRole,EndPointUser),Organization',
},
)
user_policy_moid = None
resource_values_match = False
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
user_policy_moid = intersight.result['api_response']['Moid']
#
# always_update_password
# false: compare expected vs. actual (won't check passwords)
# true: no compare
#
if module.params['state'] == 'present' and not module.params['always_update_password']:
# Create api body used to check current state
end_point_user_roles = []
for user in intersight.module.params['local_users']:
end_point_user_roles.append(
{
'Enabled': user['enable'],
'EndPointRole': [
{
'Name': user['role'],
'Type': 'IMC',
},
],
'EndPointUser': {
'Name': user['username'],
},
}
)
intersight.api_body = {
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'PasswordProperties': {
'EnforceStrongPassword': intersight.module.params['enforce_strong_password'],
'EnablePasswordExpiry': intersight.module.params['enable_password_expiry'],
'PasswordHistory': intersight.module.params['password_history'],
},
'EndPointUserRoles': end_point_user_roles,
'Organization': {
'Name': intersight.module.params['organization'],
},
}
resource_values_match = compare_values(intersight.api_body, intersight.result['api_response'])
elif module.params['state'] == 'absent':
intersight.delete_resource(
moid=user_policy_moid,
resource_path='/iam/EndPointUserPolicies',
)
user_policy_moid = None
if module.params['state'] == 'present' and not resource_values_match:
intersight.api_body = {
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'PasswordProperties': {
'EnforceStrongPassword': intersight.module.params['enforce_strong_password'],
'EnablePasswordExpiry': intersight.module.params['enable_password_expiry'],
'PasswordHistory': intersight.module.params['password_history'],
},
}
organization_moid = None
if not user_policy_moid or module.params['purge']:
# get Organization Moid which is needed when resources are created
saved_response = intersight.result['api_response']
intersight.get_resource(
resource_path='/organization/Organizations',
query_params={
'$filter': "Name eq '" + intersight.module.params['organization'] + "'",
'$select': 'Moid',
},
)
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
organization_moid = intersight.result['api_response']['Moid']
intersight.result['api_response'] = saved_response
if not user_policy_moid:
# Initial create: Organization must be set, but can't be changed after initial POST
intersight.api_body['Organization'] = {
'Moid': organization_moid,
}
elif module.params['purge']:
# update existing resource and purge any existing users
for end_point_user_role in intersight.result['api_response']['EndPointUserRoles']:
intersight.delete_resource(
moid=end_point_user_role['Moid'],
resource_path='/iam/EndPointUserRoles',
)
# configure the top-level policy resource
intersight.result['api_response'] = {}
intersight.configure_resource(
moid=user_policy_moid,
resource_path='/iam/EndPointUserPolicies',
body=intersight.api_body,
query_params={
'$filter': "Name eq '" + intersight.module.params['name'] + "'",
},
)
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
user_policy_moid = intersight.result['api_response']['Moid']
# EndPointUser local_users list config
for user in intersight.module.params['local_users']:
intersight.api_body = {
'Name': user['username'],
}
if organization_moid:
intersight.api_body['Organization'] = {
'Moid': organization_moid,
}
intersight.configure_resource(
moid=None,
resource_path='/iam/EndPointUsers',
body=intersight.api_body,
query_params={
'$filter': "Name eq '" + user['username'] + "'",
},
)
user_moid = None
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
user_moid = intersight.result['api_response']['Moid']
# GET EndPointRole Moid
intersight.get_resource(
resource_path='/iam/EndPointRoles',
query_params={
'$filter': "Name eq '" + user['role'] + "' and Type eq 'IMC'",
},
)
end_point_role_moid = None
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
end_point_role_moid = intersight.result['api_response']['Moid']
# EndPointUserRole config
intersight.api_body = {
'EndPointUser': {
'Moid': user_moid,
},
'EndPointRole': [
{
'Moid': end_point_role_moid,
}
],
'Password': user['password'],
'Enabled': user['enable'],
'EndPointUserPolicy': {
'Moid': user_policy_moid,
},
}
intersight.configure_resource(
moid=None,
resource_path='/iam/EndPointUserRoles',
body=intersight.api_body,
query_params={
'$filter': "EndPointUserPolicy.Moid eq '" + user_policy_moid + "'",
},
)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,160 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_ntp_policy
short_description: NTP policy configuration for Cisco Intersight
description:
- NTP policy configuration for Cisco Intersight.
- Used to configure NTP servers and timezone settings on Cisco Intersight managed devices.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the NTP policy.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
type: list
description:
description:
- The user-defined description of the NTP policy.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
enable:
description:
- Enable or disable NTP.
type: bool
default: true
ntp_servers:
description:
- List of NTP servers configured on the endpoint.
type: list
timezone:
description:
- Timezone of services on the endpoint.
author:
- David Soper (@dsoper2)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure NTP Policy
cisco.intersight.intersight_ntp_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: lab-ntp
description: NTP policy for lab use
tags:
- Key: Site
Value: RCDN
ntp_servers:
- ntp.esl.cisco.com
timezone: America/Chicago
- name: Delete NTP Policy
cisco.intersight.intersight_ntp_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: lab-ntp
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Name": "lab-ntp",
"ObjectType": "ntp.Policy",
"Tags": [
{
"Key": "Site",
"Value": "RCDN"
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
description=dict(type='str', aliases=['descr'], default=''),
tags=dict(type='list', default=[]),
enable=dict(type='bool', default=True),
ntp_servers=dict(type='list', default=[]),
timezone=dict(type='str', default=''),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
#
# Argument spec above, resource path, and API body should be the only code changed in each policy module
#
# Resource path used to configure policy
resource_path = '/ntp/Policies'
# Define API body used in compares or create
intersight.api_body = {
'Organization': {
'Name': intersight.module.params['organization'],
},
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'Enabled': intersight.module.params['enable'],
'NtpServers': intersight.module.params['ntp_servers'],
'Timezone': intersight.module.params['timezone'],
}
#
# Code below should be common across all policy modules
#
intersight.configure_policy_or_profile(resource_path=resource_path)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,216 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec, compare_values
from ansible.module_utils.basic import AnsibleModule
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_rest_api
short_description: REST API configuration for Cisco Intersight
description:
- Direct REST API configuration for Cisco Intersight.
- All REST API resources and properties must be specified.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
resource_path:
description:
- Resource URI being configured related to api_uri.
type: str
required: yes
query_params:
description:
- Query parameters for the Intersight API query languange.
type: dict
update_method:
description:
- The HTTP method used for update operations.
- Some Intersight resources require POST operations for modifications.
type: str
choices: [ patch, post ]
default: patch
api_body:
description:
- The paylod for API requests used to modify resources.
type: dict
list_body:
description:
- The paylod for API requests used to modify resources.
- Should be used instead of api_body if a list is required in the API payload.
type: list
return_list:
description:
- If C(yes), will return a list of API results in the api_response.
- By default only the 1st element of the API Results list is returned.
- Can only be used with GET operations.
type: bool
default: no
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
author:
- David Soper (@dsoper2)
- CiscoUcs (@CiscoUcs)
version_added: '2.8'
'''
EXAMPLES = r'''
- name: Configure Boot Policy
intersight_rest_api:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
api_key_uri: "{{ api_key_uri }}"
validate_certs: "{{ validate_certs }}"
resource_path: /boot/PrecisionPolicies
query_params:
$filter: "Name eq 'vmedia-localdisk'"
api_body: {
"Name": "vmedia-localdisk",
"ConfiguredBootMode": "Legacy",
"BootDevices": [
{
"ObjectType": "boot.VirtualMedia",
"Enabled": true,
"Name": "remote-vmedia",
"Subtype": "cimc-mapped-dvd"
},
{
"ObjectType": "boot.LocalDisk",
"Enabled": true,
"Name": "localdisk",
"Slot": "MRAID",
"Bootloader": null
}
],
}
state: present
- name: Delete Boot Policy
intersight_rest_api:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
api_key_uri: "{{ api_key_uri }}"
validate_certs: "{{ validate_certs }}"
resource_path: /boot/PrecisionPolicies
query_params:
$filter: "Name eq 'vmedia-localdisk'"
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"BootDevices": [
{
"Enabled": true,
"Name": "remote-vmedia",
"ObjectType": "boot.VirtualMedia",
"Subtype": "cimc-mapped-dvd"
},
{
"Bootloader": null,
"Enabled": true,
"Name": "boot-lun",
"ObjectType": "boot.LocalDisk",
"Slot": "MRAID"
}
],
"ConfiguredBootMode": "Legacy",
"Name": "vmedia-localdisk",
"ObjectType": "boot.PrecisionPolicy",
}
'''
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
resource_path=dict(type='str', required=True),
query_params=dict(type='dict', default={}),
update_method=dict(type='str', choices=['patch', 'post'], default='patch'),
api_body=dict(type='dict', default={}),
list_body=dict(type='list', default=[]),
return_list=dict(type='bool', default=False),
state=dict(type='str', choices=['absent', 'present'], default='present'),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
mutually_exclusive=[
['return_list', 'api_body'],
['return_list', 'state'],
['api_body', 'list_body'],
],
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
if module.params['list_body']:
module.params['api_body'] = module.params['list_body']
if module.params['update_method'] != 'post' or module.params['query_params']:
# get the current state of the resource
# skip if this is a post to /asset/DeviceClaims or similar resource without GET
intersight.get_resource(
resource_path=module.params['resource_path'],
query_params=module.params['query_params'],
return_list=module.params['return_list'],
)
# determine requested operation (config, delete, or neither (get resource only))
if module.params['state'] == 'present':
request_delete = False
# api_body implies resource configuration through post/patch
request_config = bool(module.params['api_body'])
else: # state == 'absent'
request_delete = True
request_config = False
moid = None
resource_values_match = False
if (request_config or request_delete) and intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
moid = intersight.result['api_response']['Moid']
if request_config:
resource_values_match = compare_values(module.params['api_body'], intersight.result['api_response'])
else: # request_delete
intersight.delete_resource(
moid=moid,
resource_path=module.params['resource_path'],
)
if request_config and not resource_values_match:
intersight.configure_resource(
moid=moid,
resource_path=module.params['resource_path'],
body=module.params['api_body'],
query_params=module.params['query_params'],
update_method=module.params['update_method'],
)
if module.params['return_list'] and not isinstance(intersight.result['api_response'], list):
intersight.result['api_response'] = []
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,282 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_server_profile
short_description: Server Profile configuration for Cisco Intersight
description:
- Server Profile configuration for Cisco Intersight.
- Used to configure Server Profiles with assigned servers and server policies.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the Server Profile.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
target_platform:
description:
- The platform for which the server profile is applicable.
- Can either be a server that is operating in Standalone mode or which is attached to a Fabric Interconnect (FIAttached) managed by Intersight.
choices: [Standalone, FIAttached]
default: Standalone
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
description:
description:
- The user-defined description of the Server Profile.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
assigned_server:
description:
- Managed Obect ID (MOID) of assigned server.
- Option can be omitted if user wishes to assign server later.
boot_order_policy:
description:
- Name of Boot Order Policy to associate with this profile.
imc_access_policy:
description:
- Name of IMC Access Policy to associate with this profile.
lan_connectivity_policy:
description:
- Name of LAN Connectivity Policy to associate with this profile.
local_user_policy:
description:
- Name of Local User Policy to associate with this profile.
ntp_policy:
description:
- Name of NTP Policy to associate with this profile.
storage_policy:
description:
- Name of Storage Policy to associate with this profile.
virtual_media_policy:
description:
- Name of Virtual Media Policy to associate with this profile.
author:
- David Soper (@dsoper2)
- Sid Nath (@SidNath21)
- Tse Kai "Kevin" Chan (@BrightScale)
- Soma Tummala (@SOMATUMMALA21)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure Server Profile
cisco.intersight.intersight_server_profile:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: SP-Server1
target_platform: FIAttached
tags:
- Key: Site
Value: SJC02
description: Profile for Server1
assigned_server: 5e3b517d6176752d319a9999
boot_order_policy: COS-Boot
imc_access_policy: sjc02-d23-access
lan_connectivity_policy: sjc02-d23-lan
local_user_policy: guest-admin
ntp_policy: lab-ntp
storage_policy: storage
virtual_media_policy: COS-VM
- name: Delete Server Profile
cisco.intersight.intersight_server_profile:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
name: SP-Server1
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"AssignedServer": {
"Moid": "5e3b517d6176752d319a0881",
"ObjectType": "compute.Blade",
},
"Name": "SP-IMM-6454-D23-1-1",
"ObjectType": "server.Profile",
"Tags": [
{
"Key": "Site",
"Value": "SJC02"
}
],
"TargetPlatform": "FIAttached",
"Type": "instance"
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec
def post_profile_to_policy(intersight, moid, resource_path, policy_name):
options = {
'http_method': 'get',
'resource_path': resource_path,
'query_params': {
'$filter': "Name eq '" + policy_name + "'",
},
}
response = intersight.call_api(**options)
if response.get('Results'):
# get expected policy moid from 1st list element
expected_policy_moid = response['Results'][0]['Moid']
actual_policy_moid = ''
# check any current profiles and delete if needed
options = {
'http_method': 'get',
'resource_path': resource_path,
'query_params': {
'$filter': "Profiles/any(t: t/Moid eq '" + moid + "')",
},
}
response = intersight.call_api(**options)
if response.get('Results'):
# get actual moid from 1st list element
actual_policy_moid = response['Results'][0]['Moid']
if actual_policy_moid != expected_policy_moid:
if not intersight.module.check_mode:
# delete the actual policy
options = {
'http_method': 'delete',
'resource_path': resource_path + '/' + actual_policy_moid + '/Profiles',
'moid': moid,
}
intersight.call_api(**options)
actual_policy_moid = ''
if not actual_policy_moid:
if not intersight.module.check_mode:
# post profile to the expected policy
options = {
'http_method': 'post',
'resource_path': resource_path + '/' + expected_policy_moid + '/Profiles',
'body': [
{
'ObjectType': 'server.Profile',
'Moid': moid,
}
]
}
intersight.call_api(**options)
intersight.result['changed'] = True
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
target_platform=dict(type='str', choices=['Standalone', 'FIAttached'], default='Standalone'),
tags=dict(type='list', default=[]),
description=dict(type='str', aliases=['descr'], default=''),
assigned_server=dict(type='str', default=''),
boot_order_policy=dict(type='str'),
imc_access_policy=dict(type='str'),
lan_connectivity_policy=dict(type='str'),
local_user_policy=dict(type='str'),
ntp_policy=dict(type='str'),
storage_policy=dict(type='str'),
virtual_media_policy=dict(type='str'),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
#
# Argument spec above, resource path, and API body should be the only code changed in this module
#
resource_path = '/server/Profiles'
# Define API body used in compares or create
intersight.api_body = {
'Organization': {
'Name': intersight.module.params['organization'],
},
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
}
intersight.result['api_response'] = {}
# Get assigned server information (if defined)
if intersight.module.params['assigned_server']:
intersight.get_resource(
resource_path='/compute/PhysicalSummaries',
query_params={
'$filter': "Moid eq '" + intersight.module.params['assigned_server'] + "'",
}
)
source_object_type = None
if intersight.result['api_response'].get('SourceObjectType'):
source_object_type = intersight.result['api_response']['SourceObjectType']
intersight.api_body['AssignedServer'] = {
'Moid': intersight.module.params['assigned_server'],
'ObjectType': source_object_type,
}
if intersight.module.params['target_platform'] == 'FIAttached':
intersight.api_body['TargetPlatform'] = intersight.module.params['target_platform']
# Configure the profile
moid = intersight.configure_policy_or_profile(resource_path=resource_path)
if moid and intersight.module.params['boot_order_policy']:
post_profile_to_policy(intersight, moid, resource_path='/boot/PrecisionPolicies', policy_name=intersight.module.params['boot_order_policy'])
if moid and intersight.module.params['imc_access_policy']:
post_profile_to_policy(intersight, moid, resource_path='/access/Policies', policy_name=intersight.module.params['imc_access_policy'])
if moid and intersight.module.params['lan_connectivity_policy']:
post_profile_to_policy(intersight, moid, resource_path='/vnic/LanConnectivityPolicies', policy_name=intersight.module.params['lan_connectivity_policy'])
if moid and intersight.module.params['local_user_policy']:
post_profile_to_policy(intersight, moid, resource_path='/iam/EndPointUserPolicies', policy_name=intersight.module.params['local_user_policy'])
if moid and intersight.module.params['ntp_policy']:
post_profile_to_policy(intersight, moid, resource_path='/ntp/Policies', policy_name=intersight.module.params['ntp_policy'])
if moid and intersight.module.params['storage_policy']:
post_profile_to_policy(intersight, moid, resource_path='/storage/StoragePolicies', policy_name=intersight.module.params['storage_policy'])
if moid and intersight.module.params['virtual_media_policy']:
post_profile_to_policy(intersight, moid, resource_path='/vmedia/Policies', policy_name=intersight.module.params['virtual_media_policy'])
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,171 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec
from ansible.module_utils.basic import AnsibleModule
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_target_claim
short_description: Target claim configuraiton for Cisco Intersight
description:
- Target claim configuraiton for Cisco Intersight
- Used to claim or unclaim a Target from Cisco Intersight
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
claim_code:
description:
- Claim code required for registering a new Target
- Required if I(state=present)
type: str
required: no
device_id:
description:
- Device id (serial number) of target
- Targets containing multiple Target ids (e.g. IMM) can be formatted as <target1_id>&<target2_id>
type: dict
required: yes
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
author:
- Brandon Beck (@techBeck03)
- CiscoUcs (@CiscoUcs)
version_added: '2.8'
'''
EXAMPLES = r'''
- name: Claim new Target
cisco.intersight.intersight_target_claim:
device_id: "{{ device_id }}"
claim_code: "{{ claim_code }}"
state: present
- name: Delete a Target (unclaim)
cisco.intersight.intersight_target_claim:
device_id: "{{ device_id }}"
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Account": {
"ClassId": "mo.MoRef",
"Moid": "8675309",
"ObjectType": "iam.Account",
"link": "https://www.intersight.com/api/v1/iam/Accounts/8675309"
},
"AccountMoid": "8675309",
"Ancestors": null,
"ClassId": "asset.DeviceClaim",
"CreateTime": "2021-05-10T17:32:13.522665238Z",
"Device": {
"ClassId": "mo.MoRef",
"Moid": "9035768",
"ObjectType": "asset.DeviceRegistration",
"link": "https://www.intersight.com/api/v1/asset/DeviceRegistrations/9035768"
},
"DisplayNames": {
"short": [
"FDO241604EM&FDO24161700"
]
},
"DomainGroupMoid": "5b4e48a96a636d6d346cd1c5",
"ModTime": "2021-05-10T17:32:13.522665238Z",
"Moid": "8675309",
"ObjectType": "asset.DeviceClaim",
"Owners": [
"90357688675309"
],
"PermissionResources": null,
"SecurityToken": "A95486674376E",
"SerialNumber": "FDO86753091&FDO86753092",
"SharedScope": "",
"Tags": [],
"trace_id": "NB3e883980a98adace8f7b9c2409cced1a"
}
'''
def main():
argument_spec = intersight_argument_spec
argument_spec.update(
claim_code=dict(type='str'),
device_id=dict(type='str', required=True),
state=dict(type='str', choices=['absent', 'present'], default='present'),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
required_if=[
('state', 'present', (['claim_code']), False),
]
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
# Check if device already exists in target list
target_ids = module.params['device_id'].split('&')
target_filter = ''
for idx, target_id in enumerate(target_ids):
if idx == 0:
target_filter += f"contains(TargetId,'{target_id}')"
else:
target_filter += f" or contains(TargetId,'{target_id}')"
intersight.get_resource(
resource_path='/asset/Targets',
query_params={
"$select": "TargetId,RegisteredDevice",
"$filter": target_filter,
"$expand": "RegisteredDevice($select=DeviceClaim)"
},
return_list=False,
)
if module.params['state'] == 'present':
# Send claim request if device id not already claimed
if not intersight.result['api_response']:
intersight.configure_resource(
moid=None,
resource_path='/asset/DeviceClaims',
body=dict(
SecurityToken=module.params['claim_code'],
SerialNumber=module.params['device_id']
),
query_params=None,
update_method='post'
)
elif module.params['state'] == 'absent':
# Check if target exists
if intersight.result['api_response'].get('Moid'):
intersight.delete_resource(
moid=intersight.result['api_response'].get('RegisteredDevice').get('DeviceClaim').get('Moid'),
resource_path='/asset/DeviceClaims',
)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,368 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: intersight_virtual_media_policy
short_description: Virtual Media policy configuration for Cisco Intersight
description:
- Virtual Media policy configuration for Cisco Intersight.
- Used to configure Virtual Media image mappings on Cisco Intersight managed devices.
- For more information see L(Cisco Intersight,https://intersight.com/apidocs).
extends_documentation_fragment: intersight
options:
state:
description:
- If C(present), will verify the resource is present and will create if needed.
- If C(absent), will verify the resource is absent and will delete if needed.
choices: [present, absent]
default: present
organization:
description:
- The name of the Organization this resource is assigned to.
- Profiles and Policies that are created within a Custom Organization are applicable only to devices in the same Organization.
default: default
name:
description:
- The name assigned to the NTP policy.
- The name must be between 1 and 62 alphanumeric characters, allowing special characters :-_.
required: true
tags:
description:
- List of tags in Key:<user-defined key> Value:<user-defined value> format.
type: list
descrption:
description:
- The user-defined description of the NTP policy.
- Description can contain letters(a-z, A-Z), numbers(0-9), hyphen(-), period(.), colon(:), or an underscore(_).
aliases: [descr]
enable:
description:
- Enable or disable virtual media.
type: bool
default: true
encryption:
description:
- If enabled, allows encryption of all Virtual Media communications
type: bool
default: false
low_power_usb:
description:
- If enabled, the virtual drives appear on the boot selection menu after mapping the image and rebooting the host.
type: bool
default: true
cdd_virtual_media:
description:
- CDD Virtual Media image mapping options.
suboptions:
enable:
description:
- Enable or disable CDD image mapping.
type: bool
default: true
mount_type:
description:
- Type (protocol) of network share used by the remote_hostname.
- Ensure that the remote_hostname's communication port for the mount type that you choose is accessible from the managed endpoint.
- For CIFS as your mount type, ensure port 445 (which is its communication port) on the remote_hostname is accessible.
- For HTTP, ensure port 80 is accessible.
- For HTTPS, ensure port 443 is accessible.
- For NFS, ensure port 2049 is accessible.
choices: [nfs,cifs,http,https]
required: true
volume:
description:
- A user defined name of the image mounted for mapping.
required: true
remote_hostname:
description:
- Hostname or IP address of the server hosting the virtual media image.
required: true
remote_path:
description:
- Filepath (not including the filename) of the remote image.
- Ex. mnt/SHARE/ISOS
required: true
remote_file:
description:
- Filename of the remote image.
- Ex. custom_image.iso
required: true
username:
description:
- The username for the specified Mount Type, if required.
password:
description:
- The password for the selected username, if required.
hdd_virtual_media:
description:
- HDD Virtual Media image mapping options.
suboptions:
enable:
description:
- Enable or disable HDD image mapping.
type: bool
default: false
mount_type:
description:
- Type (protocol) of network share used by the remote_hostname.
- Ensure that the remote_hostname's communication port for the mount type that you choose is accessible from the managed endpoint.
- For CIFS as your mount type, ensure port 445 (which is its communication port) on the remote_hostname is accessible.
- For HTTP, ensure port 80 is accessible.
- For HTTPS, ensure port 443 is accessible.
- For NFS, ensure port 2049 is accessible.
choices: [nfs,cifs,http,https]
required: true
volume:
description:
- A user defined name of the image mounted for mapping.
required: true
remote_hostname:
description:
- Hostname or IP address of the server hosting the virtual media image.
required: true
remote_path:
description:
- Filepath (not including the filename) of the remote image.
- Ex. mnt/SHARE/ISOS
required: true
remote_file:
description:
- Filename of the remote image.
- Ex. custom_image.iso
required: true
username:
description:
- The username for the specified Mount Type, if required.
password:
description:
- The password for the selected username, if required.
mount_options:
description:
- Mount options for the Virtual Media mapping.
- For NFS, supported options are ro, rw, nolock, noexec, soft, port=VALUE, timeo=VALUE, retry=VALUE
- For CIFS, supported options are soft, nounix, noserverino, guest
required: false
authentication_protocol:
description:
- Authentication Protocol for CIFS Mount Type
required: false
author:
- David Soper (@dsoper2)
- Sid Nath (@SidNath21)
version_added: '2.10'
'''
EXAMPLES = r'''
- name: Configure Virtual Media Policy
cisco.intersight.intersight_virtual_media_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: lab-vmedia
description: Virutal Media policy for lab use
tags:
- Key: Site
Value: RCDN
cdd_virtual_media:
mount_type: nfs
volume: nfs-cdd
remote_hostname: 172.28.224.77
remote_path: mnt/SHARE/ISOS/CENTOS
remote_file: CentOS7.iso
hdd_virtual_media:
mount_type: nfs
volume: nfs-hdd
remote_hostname: 172.28.224.77
remote_path: mnt/SHARE/ISOS/CENTOS
remote_file: CentOS7.iso
- name: Delete Virtual Media Policy
cisco.intersight.intersight_virtual_media_policy:
api_private_key: "{{ api_private_key }}"
api_key_id: "{{ api_key_id }}"
organization: DevNet
name: lab-vmedia
state: absent
'''
RETURN = r'''
api_repsonse:
description: The API response output returned by the specified resource.
returned: always
type: dict
sample:
"api_response": {
"Name": "lab-ntp",
"ObjectType": "ntp.Policy",
"Tags": [
{
"Key": "Site",
"Value": "RCDN"
}
]
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.intersight.plugins.module_utils.intersight import IntersightModule, intersight_argument_spec, compare_values
def main():
path = '/vmedia/Policies'
virtual_media_mapping = dict(
enable=dict(type='bool', default=True),
mount_type=dict(type='str', choices=['nfs', 'cifs', 'http', 'https'], required=True),
volume=dict(type='str', required=True),
remote_hostname=dict(type='str', required=True),
remote_path=dict(type='str', required=True),
remote_file=dict(type='str', required=True),
mount_options=dict(type='str', default=''),
username=dict(type='str', default=''),
password=dict(type='str', default='', no_log=True),
authentication_protocol=dict(type='str', default='none'),
)
argument_spec = intersight_argument_spec
argument_spec.update(
state=dict(type='str', choices=['present', 'absent'], default='present'),
organization=dict(type='str', default='default'),
name=dict(type='str', required=True),
description=dict(type='str', aliases=['descr'], default=''),
tags=dict(type='list', default=[]),
enable=dict(type='bool', default=True),
encryption=dict(type='bool', default=False),
low_power_usb=dict(type='bool', default=True),
cdd_virtual_media=dict(type='dict', options=virtual_media_mapping),
hdd_virtual_media=dict(type='dict', options=virtual_media_mapping),
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
)
intersight = IntersightModule(module)
intersight.result['api_response'] = {}
intersight.result['trace_id'] = ''
# Defined API body used in compares or create
intersight.api_body = {
'Organization': {
'Name': intersight.module.params['organization'],
},
'Name': intersight.module.params['name'],
'Tags': intersight.module.params['tags'],
'Description': intersight.module.params['description'],
'Enabled': intersight.module.params['enable'],
"Encryption": intersight.module.params['encryption'],
"LowPowerUsb": intersight.module.params['low_power_usb'],
'Mappings': [],
}
if intersight.module.params.get('cdd_virtual_media'):
intersight.api_body['Mappings'].append(
{
"ClassId": "vmedia.Mapping",
"ObjectType": "vmedia.Mapping",
"AuthenticationProtocol": intersight.module.params['cdd_virtual_media']['authentication_protocol'],
"DeviceType": "cdd",
"HostName": intersight.module.params['cdd_virtual_media']['remote_hostname'],
"Password": intersight.module.params['cdd_virtual_media']['password'],
"IsPasswordSet": intersight.module.params['cdd_virtual_media']['password'] != '',
"MountOptions": intersight.module.params['cdd_virtual_media']['mount_options'],
"MountProtocol": intersight.module.params['cdd_virtual_media']['mount_type'],
"RemoteFile": intersight.module.params['cdd_virtual_media']['remote_file'],
"RemotePath": intersight.module.params['cdd_virtual_media']['remote_path'],
"Username": intersight.module.params['cdd_virtual_media']['username'],
"VolumeName": intersight.module.params['cdd_virtual_media']['volume'],
}
)
if intersight.module.params.get('hdd_virtual_media'):
intersight.api_body['Mappings'].append(
{
"ClassId": "vmedia.Mapping",
"ObjectType": "vmedia.Mapping",
"AuthenticationProtocol": intersight.module.params['hdd_virtual_media']['authentication_protocol'],
"DeviceType": "hdd",
"HostName": intersight.module.params['hdd_virtual_media']['remote_hostname'],
"Password": intersight.module.params['hdd_virtual_media']['password'],
"IsPasswordSet": intersight.module.params['hdd_virtual_media']['password'] != '',
"MountOptions": intersight.module.params['hdd_virtual_media']['mount_options'],
"MountProtocol": intersight.module.params['hdd_virtual_media']['mount_type'],
"RemoteFile": intersight.module.params['hdd_virtual_media']['remote_file'],
"RemotePath": intersight.module.params['hdd_virtual_media']['remote_path'],
"Username": intersight.module.params['hdd_virtual_media']['username'],
"VolumeName": intersight.module.params['hdd_virtual_media']['volume'],
}
)
organization_moid = None
# GET Organization Moid
intersight.get_resource(
resource_path='/organization/Organizations',
query_params={
'$filter': "Name eq '" + intersight.module.params['organization'] + "'",
'$select': 'Moid',
},
)
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
organization_moid = intersight.result['api_response']['Moid']
intersight.result['api_response'] = {}
# get the current state of the resource
filter_str = "Name eq '" + intersight.module.params['name'] + "'"
filter_str += "and Organization.Moid eq '" + organization_moid + "'"
intersight.get_resource(
resource_path=path,
query_params={
'$filter': filter_str,
'$expand': 'Organization',
},
)
moid = None
resource_values_match = False
if intersight.result['api_response'].get('Moid'):
# resource exists and moid was returned
moid = intersight.result['api_response']['Moid']
if module.params['state'] == 'present':
resource_values_match = compare_values(intersight.api_body, intersight.result['api_response'])
else: # state == 'absent'
intersight.delete_resource(
moid=moid,
resource_path=path,
)
moid = None
if module.params['state'] == 'present' and not resource_values_match:
# remove read-only Organization key
intersight.api_body.pop('Organization')
if not moid:
# Organization must be set, but can't be changed after initial POST
intersight.api_body['Organization'] = {
'Moid': organization_moid,
}
intersight.configure_resource(
moid=moid,
resource_path=path,
body=intersight.api_body,
query_params={
'$filter': filter_str,
},
)
module.exit_json(**intersight.result)
if __name__ == '__main__':
main()